| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835 |
- // Copyright (C) 2004-2024 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "html-imp.h"
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <assert.h>
- static const char *border_width_kw[] = {
- "medium",
- "thick",
- "thin",
- };
- static const char *border_style_kw[] = {
- "dashed",
- "dotted",
- "double",
- "groove",
- "hidden",
- "inset",
- "none",
- "outset",
- "ridge",
- "solid",
- };
- static const char *color_kw[] = {
- "aqua",
- "black",
- "blue",
- "fuchsia",
- "gray",
- "green",
- "lime",
- "maroon",
- "navy",
- "olive",
- "orange",
- "purple",
- "red",
- "silver",
- "teal",
- "transparent",
- "white",
- "yellow",
- };
- static const char *list_style_type_kw[] = {
- "armenian",
- "circle",
- "decimal",
- "decimal-leading-zero",
- "disc",
- "georgian",
- "lower-alpha",
- "lower-greek",
- "lower-latin",
- "lower-roman",
- "none",
- "square",
- "upper-alpha",
- "upper-greek",
- "upper-latin",
- "upper-roman",
- };
- static const char *list_style_position_kw[] = {
- "inside",
- "outside",
- };
- static const char *font_style_kw[] = {
- "italic",
- "oblique",
- };
- static const char *font_variant_kw[] = {
- "small-caps",
- };
- static const char *font_weight_kw[] = {
- "bold",
- "bolder",
- "lighter",
- };
- static const char *font_size_kw[] = {
- "large",
- "larger",
- "medium",
- "small",
- "smaller",
- "x-large",
- "x-small",
- "xx-large",
- "xx-small",
- };
- /* Properties to ignore when scanning through font-family. We set font-family
- * to the full font shorthand value list because Adobe generates DS strings
- * where the font-family comes before the font-size (and not at the end as it's
- * supposed to). This lets us scan the font shorthand list without trying to
- * look up fonts named "bold", etc.
- */
- static const char *font_family_ignore[] = {
- ",",
- "/",
- "bold",
- "bolder",
- "italic",
- "large",
- "larger",
- "lighter",
- "medium",
- "oblique",
- "small",
- "small-caps",
- "smaller",
- "x-large",
- "x-small",
- "xx-large",
- "xx-small",
- };
- static int
- keyword_in_list(const char *name, const char **list, int n)
- {
- int l = 0;
- int r = n - 1;
- while (l <= r)
- {
- int m = (l + r) >> 1;
- int c = strcmp(name, list[m]);
- if (c < 0)
- r = m - 1;
- else if (c > 0)
- l = m + 1;
- else
- return 1;
- }
- return 0;
- }
- static int
- is_bold_from_font_weight(const char *weight)
- {
- return !strcmp(weight, "bold") || !strcmp(weight, "bolder") || atoi(weight) > 400;
- }
- static int
- is_italic_from_font_style(const char *style)
- {
- return !strcmp(style, "italic") || !strcmp(style, "oblique");
- }
- /*
- * Compute specificity
- */
- static int
- count_condition_ids(fz_css_condition *cond)
- {
- int n = 0;
- while (cond)
- {
- if (cond->type == '#')
- n ++;
- cond = cond->next;
- }
- return n;
- }
- static int
- count_selector_ids(fz_css_selector *sel)
- {
- int n = count_condition_ids(sel->cond);
- if (sel->left && sel->right)
- {
- n += count_selector_ids(sel->left);
- n += count_selector_ids(sel->right);
- }
- return n;
- }
- static int
- count_condition_atts(fz_css_condition *cond)
- {
- int n = 0;
- while (cond)
- {
- if (cond->type != '#' && cond->type != ':')
- n ++;
- cond = cond->next;
- }
- return n;
- }
- static int
- count_selector_atts(fz_css_selector *sel)
- {
- int n = count_condition_atts(sel->cond);
- if (sel->left && sel->right)
- {
- n += count_selector_atts(sel->left);
- n += count_selector_atts(sel->right);
- }
- return n;
- }
- static int
- count_condition_names(fz_css_condition *cond)
- {
- int n = 0;
- while (cond)
- {
- if (cond->type == ':')
- n ++;
- cond = cond->next;
- }
- return n;
- }
- static int
- count_selector_names(fz_css_selector *sel)
- {
- int n = count_condition_names(sel->cond);
- if (sel->left && sel->right)
- {
- n += count_selector_names(sel->left);
- n += count_selector_names(sel->right);
- }
- else if (sel->name)
- {
- n ++;
- }
- return n;
- }
- #define INLINE_SPECIFICITY 10000
- static int
- selector_specificity(fz_css_selector *sel, int important)
- {
- int b = count_selector_ids(sel);
- int c = count_selector_atts(sel);
- int d = count_selector_names(sel);
- return important * 1000 + b * 100 + c * 10 + d;
- }
- /*
- * Selector matching
- */
- static int
- match_att_exists_condition(fz_xml *node, const char *key)
- {
- const char *s = fz_xml_att(node, key);
- return s != NULL;
- }
- static int
- match_att_is_condition(fz_xml *node, const char *key, const char *val)
- {
- const char *att = fz_xml_att(node, key);
- return att && !strcmp(val, att);
- }
- static int
- match_att_has_condition(fz_xml *node, const char *att, const char *needle)
- {
- const char *haystack = fz_xml_att(node, att);
- const char *ss;
- size_t n;
- if (haystack) {
- ss = strstr(haystack, needle);
- if (ss)
- {
- n = strlen(needle);
- /* Look for exact matches or matching words. */
- if ((ss[n] == ' ' || ss[n] == 0) && (ss == haystack || ss[-1] == ' '))
- return 1;
- }
- }
- return 0;
- }
- static int
- match_condition(fz_css_condition *cond, fz_xml *node)
- {
- if (!cond)
- return 1;
- switch (cond->type) {
- default: return 0;
- case ':': return 0; /* don't support pseudo-classes */
- case '#': if (!match_att_is_condition(node, "id", cond->val)) return 0; break;
- case '.': if (!match_att_has_condition(node, "class", cond->val)) return 0; break;
- case '[': if (!match_att_exists_condition(node, cond->key)) return 0; break;
- case '=': if (!match_att_is_condition(node, cond->key, cond->val)) return 0; break;
- case '~': if (!match_att_has_condition(node, cond->key, cond->val)) return 0; break;
- case '|': if (!match_att_is_condition(node, cond->key, cond->val)) return 0; break;
- }
- return match_condition(cond->next, node);
- }
- static int
- match_selector(fz_css_selector *sel, fz_xml *node)
- {
- if (!node)
- return 0;
- if (sel->combine)
- {
- /* descendant */
- if (sel->combine == ' ')
- {
- fz_xml *parent = fz_xml_up(node);
- if (!parent || !match_selector(sel->right, node))
- return 0;
- while (parent)
- {
- if (match_selector(sel->left, parent))
- return 1;
- parent = fz_xml_up(parent);
- }
- return 0;
- }
- /* child */
- if (sel->combine == '>')
- {
- fz_xml *parent = fz_xml_up(node);
- if (!parent)
- return 0;
- if (!match_selector(sel->left, parent))
- return 0;
- if (!match_selector(sel->right, node))
- return 0;
- }
- /* adjacent */
- if (sel->combine == '+')
- {
- fz_xml *prev = fz_xml_prev(node);
- while (prev && !fz_xml_tag(prev))
- prev = fz_xml_prev(prev);
- if (!prev)
- return 0;
- if (!fz_xml_tag(prev))
- return 0;
- if (!match_selector(sel->left, prev))
- return 0;
- if (!match_selector(sel->right, node))
- return 0;
- }
- }
- if (sel->name)
- {
- if (!fz_xml_is_tag(node, sel->name))
- return 0;
- }
- if (sel->cond)
- {
- if (!match_condition(sel->cond, node))
- return 0;
- }
- return 1;
- }
- /*
- * Annotating nodes with properties and expanding shorthand forms.
- */
- static int
- count_values(fz_css_value *value)
- {
- int n = 0;
- while (value)
- {
- n++;
- value = value->next;
- }
- return n;
- }
- static void add_property(fz_css_match *match, int name, fz_css_value *value, int spec);
- static void
- add_shorthand_trbl(fz_css_match *match, fz_css_value *value, int spec,
- int name_t, int name_r, int name_b, int name_l)
- {
- int n = count_values(value);
- if (n == 1)
- {
- add_property(match, name_t, value, spec);
- add_property(match, name_r, value, spec);
- add_property(match, name_b, value, spec);
- add_property(match, name_l, value, spec);
- }
- if (n == 2)
- {
- fz_css_value *a = value;
- fz_css_value *b = value->next;
- add_property(match, name_t, a, spec);
- add_property(match, name_r, b, spec);
- add_property(match, name_b, a, spec);
- add_property(match, name_l, b, spec);
- }
- if (n == 3)
- {
- fz_css_value *a = value;
- fz_css_value *b = value->next;
- fz_css_value *c = value->next->next;
- add_property(match, name_t, a, spec);
- add_property(match, name_r, b, spec);
- add_property(match, name_b, c, spec);
- add_property(match, name_l, b, spec);
- }
- if (n == 4)
- {
- fz_css_value *a = value;
- fz_css_value *b = value->next;
- fz_css_value *c = value->next->next;
- fz_css_value *d = value->next->next->next;
- add_property(match, name_t, a, spec);
- add_property(match, name_r, b, spec);
- add_property(match, name_b, c, spec);
- add_property(match, name_l, d, spec);
- }
- }
- static void
- add_shorthand_margin(fz_css_match *match, fz_css_value *value, int spec)
- {
- add_shorthand_trbl(match, value, spec,
- PRO_MARGIN_TOP,
- PRO_MARGIN_RIGHT,
- PRO_MARGIN_BOTTOM,
- PRO_MARGIN_LEFT);
- }
- static void
- add_shorthand_padding(fz_css_match *match, fz_css_value *value, int spec)
- {
- add_shorthand_trbl(match, value, spec,
- PRO_PADDING_TOP,
- PRO_PADDING_RIGHT,
- PRO_PADDING_BOTTOM,
- PRO_PADDING_LEFT);
- }
- static void
- add_shorthand_border_width(fz_css_match *match, fz_css_value *value, int spec)
- {
- add_shorthand_trbl(match, value, spec,
- PRO_BORDER_TOP_WIDTH,
- PRO_BORDER_RIGHT_WIDTH,
- PRO_BORDER_BOTTOM_WIDTH,
- PRO_BORDER_LEFT_WIDTH);
- }
- static void
- add_shorthand_border_color(fz_css_match *match, fz_css_value *value, int spec)
- {
- add_shorthand_trbl(match, value, spec,
- PRO_BORDER_TOP_COLOR,
- PRO_BORDER_RIGHT_COLOR,
- PRO_BORDER_BOTTOM_COLOR,
- PRO_BORDER_LEFT_COLOR);
- }
- static void
- add_shorthand_border_style(fz_css_match *match, fz_css_value *value, int spec)
- {
- add_shorthand_trbl(match, value, spec,
- PRO_BORDER_TOP_STYLE,
- PRO_BORDER_RIGHT_STYLE,
- PRO_BORDER_BOTTOM_STYLE,
- PRO_BORDER_LEFT_STYLE);
- }
- static void
- add_shorthand_border(fz_css_match *match, fz_css_value *value, int spec, int T, int R, int B, int L)
- {
- while (value)
- {
- if (value->type == CSS_HASH)
- {
- if (T) add_property(match, PRO_BORDER_TOP_COLOR, value, spec);
- if (R) add_property(match, PRO_BORDER_RIGHT_COLOR, value, spec);
- if (B) add_property(match, PRO_BORDER_BOTTOM_COLOR, value, spec);
- if (L) add_property(match, PRO_BORDER_LEFT_COLOR, value, spec);
- }
- else if (value->type == CSS_KEYWORD)
- {
- if (keyword_in_list(value->data, border_width_kw, nelem(border_width_kw)))
- {
- if (T) add_property(match, PRO_BORDER_TOP_WIDTH, value, spec);
- if (R) add_property(match, PRO_BORDER_RIGHT_WIDTH, value, spec);
- if (B) add_property(match, PRO_BORDER_BOTTOM_WIDTH, value, spec);
- if (L) add_property(match, PRO_BORDER_LEFT_WIDTH, value, spec);
- }
- else if (keyword_in_list(value->data, border_style_kw, nelem(border_style_kw)))
- {
- if (T) add_property(match, PRO_BORDER_TOP_STYLE, value, spec);
- if (R) add_property(match, PRO_BORDER_RIGHT_STYLE, value, spec);
- if (B) add_property(match, PRO_BORDER_BOTTOM_STYLE, value, spec);
- if (L) add_property(match, PRO_BORDER_LEFT_STYLE, value, spec);
- }
- else if (keyword_in_list(value->data, color_kw, nelem(color_kw)))
- {
- if (T) add_property(match, PRO_BORDER_TOP_COLOR, value, spec);
- if (R) add_property(match, PRO_BORDER_RIGHT_COLOR, value, spec);
- if (B) add_property(match, PRO_BORDER_BOTTOM_COLOR, value, spec);
- if (L) add_property(match, PRO_BORDER_LEFT_COLOR, value, spec);
- }
- }
- else
- {
- if (T) add_property(match, PRO_BORDER_TOP_WIDTH, value, spec);
- if (R) add_property(match, PRO_BORDER_RIGHT_WIDTH, value, spec);
- if (B) add_property(match, PRO_BORDER_BOTTOM_WIDTH, value, spec);
- if (L) add_property(match, PRO_BORDER_LEFT_WIDTH, value, spec);
- }
- value = value->next;
- }
- }
- static void
- add_shorthand_list_style(fz_css_match *match, fz_css_value *value, int spec)
- {
- while (value)
- {
- if (value->type == CSS_KEYWORD)
- {
- if (keyword_in_list(value->data, list_style_type_kw, nelem(list_style_type_kw)))
- {
- add_property(match, PRO_LIST_STYLE_TYPE, value, spec);
- }
- else if (keyword_in_list(value->data, list_style_position_kw, nelem(list_style_position_kw)))
- {
- add_property(match, PRO_LIST_STYLE_POSITION, value, spec);
- }
- }
- value = value->next;
- }
- }
- static fz_css_value static_value_normal = { CSS_KEYWORD, "normal", NULL, NULL };
- static fz_css_value *
- add_shorthand_font_size(fz_css_match *match, fz_css_value *value, int spec)
- {
- /* font-size */
- add_property(match, PRO_FONT_SIZE, value, spec);
- /* / line-height */
- if (value->next && value->next->next && !strcmp(value->next->data, "/"))
- {
- value = value->next->next;
- add_property(match, PRO_LINE_HEIGHT, value, spec);
- }
- return value;
- }
- static void
- add_shorthand_font(fz_css_match *match, fz_css_value *value, int spec)
- {
- fz_css_value *font_style = NULL;
- fz_css_value *font_variant = NULL;
- fz_css_value *font_weight = NULL;
- /* add the start as font-family for most robust scanning of matching font names */
- add_property(match, PRO_FONT_FAMILY, value, spec);
- /* then look for known style/variant/weight keywords and font-size/line-height */
- for (; value; value = value->next)
- {
- /* style/variant/weight/size */
- if (value->type == CSS_KEYWORD)
- {
- if (keyword_in_list(value->data, font_style_kw, nelem(font_style_kw)))
- font_style = value;
- else if (keyword_in_list(value->data, font_variant_kw, nelem(font_variant_kw)))
- font_variant = value;
- else if (keyword_in_list(value->data, font_weight_kw, nelem(font_weight_kw)))
- font_weight = value;
- else if (keyword_in_list(value->data, font_size_kw, nelem(font_size_kw)))
- value = add_shorthand_font_size(match, value, spec);
- }
- else if (value->type == CSS_NUMBER)
- font_weight = value;
- else if (value->type == CSS_LENGTH || value->type == CSS_PERCENT)
- value = add_shorthand_font_size(match, value, spec);
- }
- /* set all properties to their initial values if not specified! */
- if (font_style)
- add_property(match, PRO_FONT_STYLE, font_style, spec);
- else
- add_property(match, PRO_FONT_STYLE, &static_value_normal, spec);
- if (font_variant)
- add_property(match, PRO_FONT_VARIANT, font_variant, spec);
- else
- add_property(match, PRO_FONT_VARIANT, &static_value_normal, spec);
- if (font_weight)
- add_property(match, PRO_FONT_WEIGHT, font_weight, spec);
- else
- add_property(match, PRO_FONT_WEIGHT, &static_value_normal, spec);
- }
- static void
- add_property(fz_css_match *match, int name, fz_css_value *value, int spec)
- {
- /* shorthand expansions: */
- switch (name)
- {
- case PRO_MARGIN:
- add_shorthand_margin(match, value, spec);
- return;
- case PRO_PADDING:
- add_shorthand_padding(match, value, spec);
- return;
- case PRO_BORDER_WIDTH:
- add_shorthand_border_width(match, value, spec);
- return;
- case PRO_BORDER_COLOR:
- add_shorthand_border_color(match, value, spec);
- return;
- case PRO_BORDER_STYLE:
- add_shorthand_border_style(match, value, spec);
- return;
- case PRO_BORDER:
- add_shorthand_border(match, value, spec, 1, 1, 1, 1);
- return;
- case PRO_BORDER_TOP:
- add_shorthand_border(match, value, spec, 1, 0, 0, 0);
- return;
- case PRO_BORDER_RIGHT:
- add_shorthand_border(match, value, spec, 0, 1, 0, 0);
- return;
- case PRO_BORDER_BOTTOM:
- add_shorthand_border(match, value, spec, 0, 0, 1, 0);
- return;
- case PRO_BORDER_LEFT:
- add_shorthand_border(match, value, spec, 0, 0, 0, 1);
- return;
- case PRO_LIST_STYLE:
- add_shorthand_list_style(match, value, spec);
- return;
- case PRO_FONT:
- add_shorthand_font(match, value, spec);
- return;
- /* TODO: background */
- }
- if (name < NUM_PROPERTIES && match->spec[name] <= spec)
- {
- match->value[name] = value;
- match->spec[name] = spec;
- }
- }
- void
- fz_match_css(fz_context *ctx, fz_css_match *match, fz_css_match *up, fz_css *css, fz_xml *node)
- {
- fz_css_rule *rule;
- fz_css_selector *sel;
- fz_css_property *prop;
- const char *s;
- int i;
- match->up = up;
- for (i = 0; i < NUM_PROPERTIES; ++i)
- {
- match->spec[i] = -1;
- match->value[i] = NULL;
- }
- for (rule = css->rule; rule; rule = rule->next)
- {
- sel = rule->selector;
- while (sel)
- {
- if (match_selector(sel, node))
- {
- for (prop = rule->declaration; prop; prop = prop->next)
- add_property(match, prop->name, prop->value, selector_specificity(sel, prop->important));
- break;
- }
- sel = sel->next;
- }
- }
- if (fz_use_document_css(ctx))
- {
- s = fz_xml_att(node, "style");
- if (s)
- {
- fz_try(ctx)
- {
- prop = fz_parse_css_properties(ctx, css->pool, s);
- while (prop)
- {
- add_property(match, prop->name, prop->value, INLINE_SPECIFICITY);
- prop = prop->next;
- }
- /* We can "leak" the property here, since it is freed along with the pool allocator. */
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "ignoring style attribute");
- }
- }
- }
- }
- void
- fz_match_css_at_page(fz_context *ctx, fz_css_match *match, fz_css *css)
- {
- fz_css_rule *rule;
- fz_css_selector *sel;
- fz_css_property *prop;
- int i;
- match->up = NULL;
- for (i = 0; i < NUM_PROPERTIES; ++i)
- {
- match->spec[i] = -1;
- match->value[i] = NULL;
- }
- for (rule = css->rule; rule; rule = rule->next)
- {
- sel = rule->selector;
- while (sel)
- {
- if (sel->name && !strcmp(sel->name, "@page"))
- {
- for (prop = rule->declaration; prop; prop = prop->next)
- add_property(match, prop->name, prop->value, selector_specificity(sel, prop->important));
- break;
- }
- sel = sel->next;
- }
- }
- }
- void
- fz_add_css_font_face(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css_property *declaration)
- {
- fz_html_font_face *custom;
- fz_css_property *prop;
- fz_font *font = NULL;
- fz_buffer *buf = NULL;
- fz_stream *stm = NULL;
- int is_bold, is_italic, is_small_caps;
- char path[2048];
- const char *family = "serif";
- const char *weight = "normal";
- const char *style = "normal";
- const char *variant = "normal";
- const char *src = NULL;
- for (prop = declaration; prop; prop = prop->next)
- {
- if (prop->name == PRO_FONT_FAMILY) family = prop->value->data;
- if (prop->name == PRO_FONT_WEIGHT) weight = prop->value->data;
- if (prop->name == PRO_FONT_STYLE) style = prop->value->data;
- if (prop->name == PRO_FONT_VARIANT) variant = prop->value->data;
- if (prop->name == PRO_SRC) src = prop->value->data;
- }
- if (!src)
- return;
- is_bold = is_bold_from_font_weight(weight);
- is_italic = is_italic_from_font_style(style);
- is_small_caps = !strcmp(variant, "small-caps");
- fz_strlcpy(path, base_uri, sizeof path);
- fz_strlcat(path, "/", sizeof path);
- fz_strlcat(path, src, sizeof path);
- fz_urldecode(path);
- for (custom = set->custom; custom; custom = custom->next)
- if (!strcmp(custom->src, path) && !strcmp(custom->family, family) &&
- custom->is_bold == is_bold &&
- custom->is_italic == is_italic &&
- custom->is_small_caps == is_small_caps)
- return; /* already loaded */
- fz_var(buf);
- fz_var(font);
- fz_var(stm);
- fz_try(ctx)
- {
- if (fz_has_archive_entry(ctx, zip, path))
- buf = fz_read_archive_entry(ctx, zip, path);
- else
- {
- stm = fz_try_open_file(ctx, src);
- if (stm == NULL)
- fz_throw(ctx, FZ_ERROR_FORMAT, "cannot locate font '%s' specified by css", src);
- buf = fz_read_all(ctx, stm, 0);
- }
- font = fz_new_font_from_buffer(ctx, NULL, buf, 0, 0);
- fz_add_html_font_face(ctx, set, family, is_bold, is_italic, is_small_caps, path, font);
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, buf);
- fz_drop_stream(ctx, stm);
- fz_drop_font(ctx, font);
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "cannot load font-face: %s", src);
- }
- }
- void
- fz_add_css_font_faces(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css *css)
- {
- fz_css_rule *rule;
- fz_css_selector *sel;
- for (rule = css->rule; rule; rule = rule->next)
- {
- if (!rule->loaded)
- {
- rule->loaded = 1;
- sel = rule->selector;
- while (sel)
- {
- if (sel->name && !strcmp(sel->name, "@font-face"))
- {
- fz_add_css_font_face(ctx, set, zip, base_uri, rule->declaration);
- break;
- }
- sel = sel->next;
- }
- }
- }
- }
- static int
- is_inheritable_property(int name)
- {
- return
- name == PRO_COLOR ||
- name == PRO_DIRECTION ||
- name == PRO_FONT_FAMILY ||
- name == PRO_FONT_STYLE ||
- name == PRO_FONT_VARIANT ||
- name == PRO_FONT_WEIGHT ||
- name == PRO_LEADING ||
- name == PRO_LETTER_SPACING ||
- name == PRO_LINE_HEIGHT ||
- name == PRO_LIST_STYLE_IMAGE ||
- name == PRO_LIST_STYLE_POSITION ||
- name == PRO_LIST_STYLE_TYPE ||
- name == PRO_ORPHANS ||
- name == PRO_OVERFLOW_WRAP ||
- name == PRO_QUOTES ||
- name == PRO_TEXT_ALIGN ||
- name == PRO_TEXT_INDENT ||
- name == PRO_TEXT_TRANSFORM ||
- name == PRO_VISIBILITY ||
- name == PRO_WHITE_SPACE ||
- name == PRO_WIDOWS ||
- name == PRO_WORD_SPACING ||
- // Strictly speaking, text-decoration is not an inherited property,
- // but since when drawing an underlined element, all children are also underlined,
- // we may as well make it inherited.
- name == PRO_TEXT_DECORATION;
- }
- static fz_css_value *
- value_from_inheritable_property(fz_css_match *match, int name)
- {
- fz_css_value *value = match->value[name];
- if (match->up)
- {
- if (value && !strcmp(value->data, "inherit"))
- return value_from_inheritable_property(match->up, name);
- if (!value)
- return value_from_inheritable_property(match->up, name);
- }
- return value;
- }
- static fz_css_value *
- value_from_property(fz_css_match *match, int name)
- {
- fz_css_value *value = match->value[name];
- if (match->up)
- {
- if (value && !strcmp(value->data, "inherit"))
- if (name != PRO_FONT_SIZE) /* never inherit 'font-size' textually */
- return value_from_property(match->up, name);
- if (!value && is_inheritable_property(name))
- return value_from_inheritable_property(match->up, name);
- }
- return value;
- }
- static const char *
- string_from_property(fz_css_match *match, int name, const char *initial)
- {
- fz_css_value *value = value_from_property(match, name);
- if (!value)
- return initial;
- return value->data;
- }
- static fz_css_number
- make_number(float v, int u)
- {
- fz_css_number n;
- n.value = v;
- n.unit = u;
- return n;
- }
- static fz_css_number
- make_undefined_number(void)
- {
- fz_css_number n;
- n.value = 0;
- n.unit = N_UNDEFINED;
- return n;
- }
- /* Fast but inaccurate strtof. */
- static float
- fz_css_strtof(char *s, char **endptr)
- {
- float sign = 1;
- float v = 0;
- float n = 0;
- float d = 1;
- if (*s == '-')
- {
- sign = -1;
- ++s;
- }
- while (*s >= '0' && *s <= '9')
- {
- v = v * 10 + (*s - '0');
- ++s;
- }
- if (*s == '.')
- {
- ++s;
- while (*s >= '0' && *s <= '9')
- {
- n = n * 10 + (*s - '0');
- d = d * 10;
- ++s;
- }
- v += n / d;
- }
- if (endptr)
- *endptr = s;
- return sign * v;
- }
- static fz_css_number
- number_from_value(fz_css_value *value, float initial, int initial_unit)
- {
- char *p;
- if (!value)
- return make_number(initial, initial_unit);
- if (value->type == CSS_PERCENT)
- return make_number(fz_css_strtof(value->data, NULL), N_PERCENT);
- if (value->type == CSS_NUMBER)
- return make_number(fz_css_strtof(value->data, NULL), N_NUMBER);
- if (value->type == CSS_LENGTH)
- {
- float x = fz_css_strtof(value->data, &p);
- if (p[0] == 'e' && p[1] == 'm' && p[2] == 0)
- return make_number(x, N_SCALE);
- if (p[0] == 'e' && p[1] == 'x' && p[2] == 0)
- return make_number(x / 2, N_SCALE);
- if (p[0] == 'i' && p[1] == 'n' && p[2] == 0)
- return make_number(x * 72, N_LENGTH);
- if (p[0] == 'c' && p[1] == 'm' && p[2] == 0)
- return make_number(x * 7200 / 254, N_LENGTH);
- if (p[0] == 'm' && p[1] == 'm' && p[2] == 0)
- return make_number(x * 720 / 254, N_LENGTH);
- if (p[0] == 'p' && p[1] == 'c' && p[2] == 0)
- return make_number(x * 12, N_LENGTH);
- if (p[0] == 'p' && p[1] == 't' && p[2] == 0)
- return make_number(x, N_LENGTH);
- if (p[0] == 'p' && p[1] == 'x' && p[2] == 0)
- return make_number(x, N_LENGTH);
- /* FIXME: 'rem' should be 'em' of root element. This is a bad approximation. */
- if (p[0] == 'r' && p[1] == 'e' && p[2] == 'm' && p[3] == 0)
- return make_number(x * 16, N_LENGTH);
- /* FIXME: 'ch' should be width of '0' character. This is an approximation. */
- if (p[0] == 'c' && p[1] == 'h' && p[2] == 0)
- return make_number(x / 2, N_LENGTH);
- return make_number(x, N_LENGTH);
- }
- if (value->type == CSS_KEYWORD)
- {
- if (!strcmp(value->data, "auto"))
- return make_number(0, N_AUTO);
- }
- return make_number(initial, initial_unit);
- }
- static fz_css_number
- number_from_property(fz_css_match *match, int property, float initial, int initial_unit)
- {
- return number_from_value(value_from_property(match, property), initial, initial_unit);
- }
- static fz_css_number
- border_width_from_property(fz_css_match *match, int property)
- {
- fz_css_value *value = value_from_property(match, property);
- if (value)
- {
- if (!strcmp(value->data, "thin"))
- return make_number(1, N_LENGTH);
- if (!strcmp(value->data, "medium"))
- return make_number(2, N_LENGTH);
- if (!strcmp(value->data, "thick"))
- return make_number(4, N_LENGTH);
- return number_from_value(value, 0, N_LENGTH);
- }
- return make_number(2, N_LENGTH); /* initial: 'medium' */
- }
- static int
- border_style_from_property(fz_css_match *match, int property)
- {
- fz_css_value *value = value_from_property(match, property);
- if (value)
- {
- if (!strcmp(value->data, "none")) return BS_NONE;
- else if (!strcmp(value->data, "hidden")) return BS_NONE;
- else if (!strcmp(value->data, "solid")) return BS_SOLID;
- }
- return BS_NONE;
- }
- int fz_css_number_defined(fz_css_number number)
- {
- return number.unit != N_UNDEFINED;
- }
- float
- fz_from_css_number(fz_css_number number, float em, float percent_value, float auto_value)
- {
- switch (number.unit) {
- default:
- case N_NUMBER: return number.value;
- case N_LENGTH: return number.value;
- case N_SCALE: return number.value * em;
- case N_PERCENT: return number.value * 0.01f * percent_value;
- case N_AUTO: return auto_value;
- }
- }
- float
- fz_from_css_number_scale(fz_css_number number, float scale)
- {
- switch (number.unit) {
- default:
- case N_NUMBER: return number.value * scale;
- case N_LENGTH: return number.value;
- case N_SCALE: return number.value * scale;
- case N_PERCENT: return number.value * 0.01f * scale;
- case N_AUTO: return scale;
- }
- }
- static fz_css_color
- make_color(int r, int g, int b, int a)
- {
- fz_css_color c;
- c.r = r < 0 ? 0 : r > 255 ? 255 : r;
- c.g = g < 0 ? 0 : g > 255 ? 255 : g;
- c.b = b < 0 ? 0 : b > 255 ? 255 : b;
- c.a = a < 0 ? 0 : a > 255 ? 255 : a;
- return c;
- }
- static int tohex(int c)
- {
- if (c - '0' < 10)
- return c - '0';
- return (c | 32) - 'a' + 10;
- }
- static fz_css_color
- color_from_value(fz_css_value *value, fz_css_color initial)
- {
- if (!value)
- return initial;
- if (value->type == CSS_HASH)
- {
- int r, g, b, a;
- size_t n;
- hex_color:
- n = strlen(value->data);
- if (n == 3)
- {
- r = tohex(value->data[0]) * 16 + tohex(value->data[0]);
- g = tohex(value->data[1]) * 16 + tohex(value->data[1]);
- b = tohex(value->data[2]) * 16 + tohex(value->data[2]);
- a = 255;
- }
- else if (n == 4)
- {
- r = tohex(value->data[0]) * 16 + tohex(value->data[0]);
- g = tohex(value->data[1]) * 16 + tohex(value->data[1]);
- b = tohex(value->data[2]) * 16 + tohex(value->data[2]);
- a = tohex(value->data[3]) * 16 + tohex(value->data[3]);
- }
- else if (n == 6)
- {
- r = tohex(value->data[0]) * 16 + tohex(value->data[1]);
- g = tohex(value->data[2]) * 16 + tohex(value->data[3]);
- b = tohex(value->data[4]) * 16 + tohex(value->data[5]);
- a = 255;
- }
- else if (n == 8)
- {
- r = tohex(value->data[0]) * 16 + tohex(value->data[1]);
- g = tohex(value->data[2]) * 16 + tohex(value->data[3]);
- b = tohex(value->data[4]) * 16 + tohex(value->data[5]);
- a = tohex(value->data[6]) * 16 + tohex(value->data[7]);
- }
- else
- {
- r = g = b = 0;
- a = 255;
- }
- return make_color(r, g, b, a);
- }
- if (value->type == '(' && !strcmp(value->data, "rgb"))
- {
- fz_css_value *vr, *vg, *vb;
- int r, g, b;
- vr = value->args;
- vg = vr && vr->next ? vr->next->next : NULL; /* skip the ',' nodes */
- vb = vg && vg->next ? vg->next->next : NULL; /* skip the ',' nodes */
- r = fz_from_css_number(number_from_value(vr, 0, N_NUMBER), 255, 255, 0);
- g = fz_from_css_number(number_from_value(vg, 0, N_NUMBER), 255, 255, 0);
- b = fz_from_css_number(number_from_value(vb, 0, N_NUMBER), 255, 255, 0);
- return make_color(r, g, b, 255);
- }
- if (value->type == '(' && !strcmp(value->data, "rgba"))
- {
- fz_css_value *vr, *vg, *vb, *va;
- int r, g, b, a;
- vr = value->args;
- vg = vr && vr->next ? vr->next->next : NULL; /* skip the ',' nodes */
- vb = vg && vg->next ? vg->next->next : NULL; /* skip the ',' nodes */
- va = vb && vb->next ? vb->next->next : NULL; /* skip the ',' nodes */
- r = fz_from_css_number(number_from_value(vr, 0, N_NUMBER), 255, 255, 0);
- g = fz_from_css_number(number_from_value(vg, 0, N_NUMBER), 255, 255, 0);
- b = fz_from_css_number(number_from_value(vb, 0, N_NUMBER), 255, 255, 0);
- a = fz_from_css_number(number_from_value(va, 0, N_NUMBER), 255, 255, 255);
- return make_color(r, g, b, a);
- }
- if (value->type == CSS_KEYWORD)
- {
- if (!strcmp(value->data, "transparent"))
- return make_color(0, 0, 0, 0);
- if (!strcmp(value->data, "maroon"))
- return make_color(0x80, 0x00, 0x00, 255);
- if (!strcmp(value->data, "red"))
- return make_color(0xFF, 0x00, 0x00, 255);
- if (!strcmp(value->data, "orange"))
- return make_color(0xFF, 0xA5, 0x00, 255);
- if (!strcmp(value->data, "yellow"))
- return make_color(0xFF, 0xFF, 0x00, 255);
- if (!strcmp(value->data, "olive"))
- return make_color(0x80, 0x80, 0x00, 255);
- if (!strcmp(value->data, "purple"))
- return make_color(0x80, 0x00, 0x80, 255);
- if (!strcmp(value->data, "fuchsia"))
- return make_color(0xFF, 0x00, 0xFF, 255);
- if (!strcmp(value->data, "white"))
- return make_color(0xFF, 0xFF, 0xFF, 255);
- if (!strcmp(value->data, "lime"))
- return make_color(0x00, 0xFF, 0x00, 255);
- if (!strcmp(value->data, "green"))
- return make_color(0x00, 0x80, 0x00, 255);
- if (!strcmp(value->data, "navy"))
- return make_color(0x00, 0x00, 0x80, 255);
- if (!strcmp(value->data, "blue"))
- return make_color(0x00, 0x00, 0xFF, 255);
- if (!strcmp(value->data, "aqua"))
- return make_color(0x00, 0xFF, 0xFF, 255);
- if (!strcmp(value->data, "teal"))
- return make_color(0x00, 0x80, 0x80, 255);
- if (!strcmp(value->data, "black"))
- return make_color(0x00, 0x00, 0x00, 255);
- if (!strcmp(value->data, "silver"))
- return make_color(0xC0, 0xC0, 0xC0, 255);
- if (!strcmp(value->data, "gray"))
- return make_color(0x80, 0x80, 0x80, 255);
- goto hex_color; /* last ditch attempt: maybe it's a #XXXXXX color without the # */
- }
- return initial;
- }
- static fz_css_color
- color_from_property(fz_css_match *match, int property, fz_css_color initial)
- {
- return color_from_value(value_from_property(match, property), initial);
- }
- static fz_css_color
- color_from_properties(fz_css_match *match, int property, int property2, fz_css_color initial)
- {
- fz_css_value *value = value_from_property(match, property);
- if (value == NULL)
- value = value_from_property(match, property2);
- return color_from_value(value, initial);
- }
- int
- fz_get_css_match_display(fz_css_match *match)
- {
- fz_css_value *value = value_from_property(match, PRO_DISPLAY);
- if (value)
- {
- if (!strcmp(value->data, "none"))
- return DIS_NONE;
- if (!strcmp(value->data, "inline"))
- return DIS_INLINE;
- if (!strcmp(value->data, "block"))
- return DIS_BLOCK;
- if (!strcmp(value->data, "list-item"))
- return DIS_LIST_ITEM;
- if (!strcmp(value->data, "inline-block"))
- return DIS_INLINE_BLOCK;
- if (!strcmp(value->data, "table"))
- return DIS_TABLE;
- if (!strcmp(value->data, "table-row"))
- return DIS_TABLE_ROW;
- if (!strcmp(value->data, "table-cell"))
- return DIS_TABLE_CELL;
- if (!strcmp(value->data, "table-row-group"))
- return DIS_TABLE_GROUP;
- if (!strcmp(value->data, "table-header-group"))
- return DIS_TABLE_GROUP;
- if (!strcmp(value->data, "table-footer-group"))
- return DIS_TABLE_GROUP;
- if (!strcmp(value->data, "table-column-group"))
- return DIS_NONE;
- if (!strcmp(value->data, "table-column"))
- return DIS_NONE;
- }
- return DIS_INLINE;
- }
- static int
- white_space_from_property(fz_css_match *match)
- {
- fz_css_value *value = value_from_property(match, PRO_WHITE_SPACE);
- if (value)
- {
- if (!strcmp(value->data, "normal")) return WS_NORMAL;
- else if (!strcmp(value->data, "pre")) return WS_PRE;
- else if (!strcmp(value->data, "nowrap")) return WS_NOWRAP;
- else if (!strcmp(value->data, "pre-wrap")) return WS_PRE_WRAP;
- else if (!strcmp(value->data, "pre-line")) return WS_PRE_LINE;
- }
- return WS_NORMAL;
- }
- static int
- text_decoration_from_property(fz_css_match *match)
- {
- fz_css_value *value = value_from_property(match, PRO_TEXT_DECORATION);
- if (value)
- {
- if (!strcmp(value->data, "underline")) return TD_UNDERLINE;
- if (!strcmp(value->data, "line-through")) return TD_LINE_THROUGH;
- }
- return TD_NONE;
- }
- static int
- visibility_from_property(fz_css_match *match)
- {
- fz_css_value *value = value_from_property(match, PRO_VISIBILITY);
- if (value)
- {
- if (!strcmp(value->data, "visible")) return V_VISIBLE;
- else if (!strcmp(value->data, "hidden")) return V_HIDDEN;
- else if (!strcmp(value->data, "collapse")) return V_COLLAPSE;
- }
- return V_VISIBLE;
- }
- static int
- page_break_from_property(fz_css_match *match, int prop)
- {
- fz_css_value *value = value_from_property(match, prop);
- if (value)
- {
- if (!strcmp(value->data, "auto")) return PB_AUTO;
- else if (!strcmp(value->data, "always")) return PB_ALWAYS;
- else if (!strcmp(value->data, "avoid")) return PB_AVOID;
- else if (!strcmp(value->data, "left")) return PB_LEFT;
- else if (!strcmp(value->data, "right")) return PB_RIGHT;
- }
- return PB_AUTO;
- }
- void
- fz_default_css_style(fz_context *ctx, fz_css_style *style)
- {
- memset(style, 0, sizeof *style);
- style->visibility = V_VISIBLE;
- style->text_align = TA_LEFT;
- style->vertical_align = VA_BASELINE;
- style->white_space = WS_NORMAL;
- style->list_style_type = LST_DISC;
- style->font_size = make_number(1, N_SCALE);
- style->width = make_number(0, N_AUTO);
- style->height = make_number(0, N_AUTO);
- style->leading = make_undefined_number();
- }
- void
- fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style, fz_css_match *match)
- {
- fz_css_value *value;
- fz_css_color black = { 0, 0, 0, 255 };
- fz_css_color transparent = { 0, 0, 0, 0 };
- fz_default_css_style(ctx, style);
- style->visibility = visibility_from_property(match);
- style->white_space = white_space_from_property(match);
- style->text_decoration = text_decoration_from_property(match);
- style->page_break_before = page_break_from_property(match, PRO_PAGE_BREAK_BEFORE);
- style->page_break_after = page_break_from_property(match, PRO_PAGE_BREAK_AFTER);
- value = value_from_property(match, PRO_TEXT_ALIGN);
- if (value)
- {
- if (!strcmp(value->data, "left")) style->text_align = TA_LEFT;
- else if (!strcmp(value->data, "right")) style->text_align = TA_RIGHT;
- else if (!strcmp(value->data, "center")) style->text_align = TA_CENTER;
- else if (!strcmp(value->data, "justify")) style->text_align = TA_JUSTIFY;
- }
- value = value_from_property(match, PRO_VERTICAL_ALIGN);
- if (value)
- {
- if (!strcmp(value->data, "baseline")) style->vertical_align = VA_BASELINE;
- else if (!strcmp(value->data, "sub")) style->vertical_align = VA_SUB;
- else if (!strcmp(value->data, "super")) style->vertical_align = VA_SUPER;
- else if (!strcmp(value->data, "top")) style->vertical_align = VA_TOP;
- else if (!strcmp(value->data, "bottom")) style->vertical_align = VA_BOTTOM;
- else if (!strcmp(value->data, "text-top")) style->vertical_align = VA_TEXT_TOP;
- else if (!strcmp(value->data, "text-bottom")) style->vertical_align = VA_TEXT_BOTTOM;
- }
- value = value_from_property(match, PRO_FONT_SIZE);
- if (value)
- {
- /* absolute-size */
- if (!strcmp(value->data, "xx-large")) style->font_size = make_number(1.73f, N_SCALE);
- else if (!strcmp(value->data, "x-large")) style->font_size = make_number(1.44f, N_SCALE);
- else if (!strcmp(value->data, "large")) style->font_size = make_number(1.2f, N_SCALE);
- else if (!strcmp(value->data, "medium")) style->font_size = make_number(1.0f, N_SCALE);
- else if (!strcmp(value->data, "small")) style->font_size = make_number(0.83f, N_SCALE);
- else if (!strcmp(value->data, "x-small")) style->font_size = make_number(0.69f, N_SCALE);
- else if (!strcmp(value->data, "xx-small")) style->font_size = make_number(0.69f, N_SCALE);
- /* relative-size */
- else if (!strcmp(value->data, "larger")) style->font_size = make_number(1.2f, N_SCALE);
- else if (!strcmp(value->data, "smaller")) style->font_size = make_number(1/1.2f, N_SCALE);
- /* percentage */
- else if (value->type == CSS_PERCENT) style->font_size = number_from_value(value, 12, N_LENGTH);
- /* length */
- else if (value->type == CSS_LENGTH) style->font_size = number_from_value(value, 12, N_LENGTH);
- /* default to 1em */
- else style->font_size = make_number(1, N_SCALE);
- }
- else
- {
- style->font_size = make_number(1, N_SCALE);
- }
- value = value_from_property(match, PRO_LIST_STYLE_TYPE);
- if (value)
- {
- if (!strcmp(value->data, "none")) style->list_style_type = LST_NONE;
- else if (!strcmp(value->data, "disc")) style->list_style_type = LST_DISC;
- else if (!strcmp(value->data, "circle")) style->list_style_type = LST_CIRCLE;
- else if (!strcmp(value->data, "square")) style->list_style_type = LST_SQUARE;
- else if (!strcmp(value->data, "decimal")) style->list_style_type = LST_DECIMAL;
- else if (!strcmp(value->data, "decimal-leading-zero")) style->list_style_type = LST_DECIMAL_ZERO;
- else if (!strcmp(value->data, "lower-roman")) style->list_style_type = LST_LC_ROMAN;
- else if (!strcmp(value->data, "upper-roman")) style->list_style_type = LST_UC_ROMAN;
- else if (!strcmp(value->data, "lower-greek")) style->list_style_type = LST_LC_GREEK;
- else if (!strcmp(value->data, "upper-greek")) style->list_style_type = LST_UC_GREEK;
- else if (!strcmp(value->data, "lower-latin")) style->list_style_type = LST_LC_LATIN;
- else if (!strcmp(value->data, "upper-latin")) style->list_style_type = LST_UC_LATIN;
- else if (!strcmp(value->data, "lower-alpha")) style->list_style_type = LST_LC_ALPHA;
- else if (!strcmp(value->data, "upper-alpha")) style->list_style_type = LST_UC_ALPHA;
- else if (!strcmp(value->data, "armenian")) style->list_style_type = LST_ARMENIAN;
- else if (!strcmp(value->data, "georgian")) style->list_style_type = LST_GEORGIAN;
- }
- value = value_from_property(match, PRO_OVERFLOW_WRAP);
- if (value)
- {
- if (!strcmp(value->data, "break-word")) style->overflow_wrap = OVERFLOW_WRAP_BREAK_WORD;
- else style->overflow_wrap = OVERFLOW_WRAP_NORMAL;
- }
- style->line_height = number_from_property(match, PRO_LINE_HEIGHT, 1.2f, N_SCALE);
- style->leading = number_from_property(match, PRO_LEADING, 0, N_UNDEFINED);
- style->text_indent = number_from_property(match, PRO_TEXT_INDENT, 0, N_LENGTH);
- style->text_stroke_width = number_from_property(match, PRO_TEXT_STROKE_WIDTH, 0, N_LENGTH);
- style->width = number_from_property(match, PRO_WIDTH, 0, N_AUTO);
- style->height = number_from_property(match, PRO_HEIGHT, 0, N_AUTO);
- style->margin[0] = number_from_property(match, PRO_MARGIN_TOP, 0, N_LENGTH);
- style->margin[1] = number_from_property(match, PRO_MARGIN_RIGHT, 0, N_LENGTH);
- style->margin[2] = number_from_property(match, PRO_MARGIN_BOTTOM, 0, N_LENGTH);
- style->margin[3] = number_from_property(match, PRO_MARGIN_LEFT, 0, N_LENGTH);
- style->padding[0] = number_from_property(match, PRO_PADDING_TOP, 0, N_LENGTH);
- style->padding[1] = number_from_property(match, PRO_PADDING_RIGHT, 0, N_LENGTH);
- style->padding[2] = number_from_property(match, PRO_PADDING_BOTTOM, 0, N_LENGTH);
- style->padding[3] = number_from_property(match, PRO_PADDING_LEFT, 0, N_LENGTH);
- style->color = color_from_property(match, PRO_COLOR, black);
- style->text_fill_color = color_from_properties(match, PRO_TEXT_FILL_COLOR, PRO_COLOR, black);
- style->text_stroke_color = color_from_property(match, PRO_TEXT_STROKE_COLOR, transparent);
- style->background_color = color_from_property(match, PRO_BACKGROUND_COLOR, transparent);
- style->border_spacing = number_from_property(match, PRO_BORDER_SPACING, 0, N_LENGTH);
- style->border_style_0 = border_style_from_property(match, PRO_BORDER_TOP_STYLE);
- style->border_style_1 = border_style_from_property(match, PRO_BORDER_RIGHT_STYLE);
- style->border_style_2 = border_style_from_property(match, PRO_BORDER_BOTTOM_STYLE);
- style->border_style_3 = border_style_from_property(match, PRO_BORDER_LEFT_STYLE);
- style->border_color[0] = color_from_property(match, PRO_BORDER_TOP_COLOR, style->color);
- style->border_color[1] = color_from_property(match, PRO_BORDER_RIGHT_COLOR, style->color);
- style->border_color[2] = color_from_property(match, PRO_BORDER_BOTTOM_COLOR, style->color);
- style->border_color[3] = color_from_property(match, PRO_BORDER_LEFT_COLOR, style->color);
- style->border_width[0] = border_width_from_property(match, PRO_BORDER_TOP_WIDTH);
- style->border_width[1] = border_width_from_property(match, PRO_BORDER_RIGHT_WIDTH);
- style->border_width[2] = border_width_from_property(match, PRO_BORDER_BOTTOM_WIDTH);
- style->border_width[3] = border_width_from_property(match, PRO_BORDER_LEFT_WIDTH);
- {
- const char *font_weight = string_from_property(match, PRO_FONT_WEIGHT, "normal");
- const char *font_style = string_from_property(match, PRO_FONT_STYLE, "normal");
- const char *font_variant = string_from_property(match, PRO_FONT_VARIANT, "normal");
- int is_bold = is_bold_from_font_weight(font_weight);
- int is_italic = is_italic_from_font_style(font_style);
- style->small_caps = !strcmp(font_variant, "small-caps");
- value = value_from_property(match, PRO_FONT_FAMILY);
- for (; value; value = value->next)
- {
- /* ignore numbers and keywords used in font short-hand syntax */
- if (value->type == CSS_STRING || (value->type == CSS_KEYWORD && !keyword_in_list(value->data, font_family_ignore, nelem(font_family_ignore))))
- {
- style->font = fz_load_html_font(ctx, set, value->data, is_bold, is_italic, style->small_caps);
- if (style->font)
- break;
- }
- }
- if (!style->font)
- style->font = fz_load_html_font(ctx, set, "serif", is_bold, is_italic, style->small_caps);
- }
- }
- #ifdef DEBUG_CSS_SPLAY
- static void
- do_verify_splay(const fz_css_style_splay *x)
- {
- printf("%x<", x);
- if (x->lt)
- {
- assert(memcmp(&x->lt->style, &x->style, sizeof(x->style)) < 0);
- assert(x->lt->up == x);
- do_verify_splay(x->lt);
- }
- printf(",");
- if (x->gt)
- {
- assert(memcmp(&x->gt->style, &x->style, sizeof(x->style)) > 0);
- assert(x->gt->up == x);
- do_verify_splay(x->gt);
- }
- printf(">\n");
- }
- static void
- verify_splay(const fz_css_style_splay *x)
- {
- if (x == NULL)
- return;
- assert(x->up == NULL);
- do_verify_splay(x);
- printf("-----\n");
- }
- #endif
- const fz_css_style *
- fz_css_enlist(fz_context *ctx, const fz_css_style *style, fz_css_style_splay **tree, fz_pool *pool)
- {
- fz_css_style_splay **current = tree;
- fz_css_style_splay *x;
- fz_css_style_splay *y = NULL;
- /* Search for a match in the tree, if there is one, or for
- * the insertion point, if there is not. */
- while (*current != NULL)
- {
- int cmp = memcmp(style, &(*current)->style, sizeof(*style));
- if (cmp == 0)
- {
- /* We have a match - break out and do move to root. */
- break;
- }
- y = (*current);
- if (cmp < 0)
- current = &y->lt;
- else
- current = &y->gt;
- }
- /* Create one if needed */
- if (*current == NULL)
- {
- x = *current = fz_pool_alloc(ctx, pool, sizeof(*y));
- x->style = *style;
- x->up = y;
- x->lt = NULL;
- x->gt = NULL;
- }
- else
- x = *current;
- /* Now move to root */
- /*
- The splaying steps used:
- Case 1: |a) z x b) z x
- | y D => A y A y y D
- | x C B z B x => z C
- | A B C D C D A B
- Case 2: |a) z x b) z x
- | y D => y z A y => z y
- | A x A B C D x D A B C D
- | B C B C
- Case 3: |a) y x b) y x
- | x C => A y A x => y C
- | A B B C B C A B
- */
- #ifdef DEBUG_CSS_SPLAY
- printf("BEFORE\n");
- verify_splay(*tree);
- #endif
- while ((y = x->up) != NULL ) /* While we're not at the root */
- {
- fz_css_style_splay *z = y->up;
- y->up = x;
- if (z == NULL)
- {
- if (y->lt == x) /* Case 3a */
- {
- y->lt = x->gt;
- if (y->lt)
- y->lt->up = y;
- x->gt = y;
- }
- else /* Case 3b */
- {
- y->gt = x->lt;
- if (y->gt)
- y->gt->up = y;
- x->lt = y;
- }
- x->up = NULL;
- break;
- }
- x->up = z->up;
- if (z->up)
- {
- if (z->up->lt == z)
- z->up->lt = x;
- else
- z->up->gt = x;
- }
- if (z->lt == y)
- {
- if (y->lt == x) /* Case 1a */
- {
- z->lt = y->gt;
- if (z->lt)
- z->lt->up = z;
- y->lt = x->gt;
- if (y->lt)
- y->lt->up = y;
- y->gt = z;
- z->up = y;
- x->gt = y;
- }
- else /* Case 2a */
- {
- y->gt = x->lt;
- if (y->gt)
- y->gt->up = y;
- z->lt = x->gt;
- if (z->lt)
- z->lt->up = z;
- x->lt = y;
- x->gt = z;
- z->up = x;
- }
- }
- else
- {
- if (y->gt == x) /* Case 1b */
- {
- z->gt = y->lt;
- if (z->gt)
- z->gt->up = z;
- y->gt = x->lt;
- if (y->gt)
- y->gt->up = y;
- y->lt = z;
- z->up = y;
- x->lt = y;
- }
- else /* Case 2b */
- {
- z->gt = x->lt;
- if (z->gt)
- z->gt->up = z;
- y->lt = x->gt;
- if (y->lt)
- y->lt->up = y;
- x->gt = y;
- x->lt = z;
- z->up = x;
- }
- }
- }
- *tree = x;
- #ifdef DEBUG_CSS_SPLAY
- printf("AFTER\n");
- verify_splay(x);
- #endif
- return &x->style;
- }
- /*
- * Pretty printing
- */
- static void print_value(fz_css_value *val)
- {
- printf("%s", val->data);
- if (val->args)
- {
- printf("(");
- print_value(val->args);
- printf(")");
- }
- if (val->next)
- {
- printf(" ");
- print_value(val->next);
- }
- }
- static void print_property(fz_css_property *prop)
- {
- printf("\t%s: ", fz_css_property_name(prop->name));
- print_value(prop->value);
- if (prop->important)
- printf(" !important");
- printf(";\n");
- }
- static void print_condition(fz_css_condition *cond)
- {
- if (cond->type == '=')
- printf("[%s=%s]", cond->key, cond->val);
- else if (cond->type == '[')
- printf("[%s]", cond->key);
- else
- printf("%c%s", cond->type, cond->val);
- if (cond->next)
- print_condition(cond->next);
- }
- static void print_selector(fz_css_selector *sel)
- {
- if (sel->combine)
- {
- print_selector(sel->left);
- if (sel->combine == ' ')
- printf(" ");
- else
- printf(" %c ", sel->combine);
- print_selector(sel->right);
- }
- else if (sel->name)
- printf("%s", sel->name);
- else
- printf("*");
- if (sel->cond)
- {
- print_condition(sel->cond);
- }
- }
- static void print_rule(fz_css_rule *rule)
- {
- fz_css_selector *sel;
- fz_css_property *prop;
- for (sel = rule->selector; sel; sel = sel->next)
- {
- print_selector(sel);
- printf(" /* %d */", selector_specificity(sel, 0));
- if (sel->next)
- printf(", ");
- }
- printf("\n{\n");
- for (prop = rule->declaration; prop; prop = prop->next)
- {
- print_property(prop);
- }
- printf("}\n");
- }
- void
- fz_debug_css(fz_context *ctx, fz_css *css)
- {
- fz_css_rule *rule = css->rule;
- while (rule)
- {
- print_rule(rule);
- rule = rule->next;
- }
- }
|