diff --git a/packages/core/src/vivliostyle/css-cascade.ts b/packages/core/src/vivliostyle/css-cascade.ts index a4b26472d..afdca59f8 100644 --- a/packages/core/src/vivliostyle/css-cascade.ts +++ b/packages/core/src/vivliostyle/css-cascade.ts @@ -273,7 +273,7 @@ export class CascadeValue { if (propName && Css.isCustomPropName(propName)) { return this.value; } - return CssParser.evaluateCSSToCSS(context, this.value, propName); + return evaluateCSSToCSS(context, this.value, propName); } isEnabled(context: Exprs.Context): boolean { @@ -576,7 +576,7 @@ export class InheritanceVisitor extends Css.FilterVisitor { override visitExpr(expr: Css.Expr): Css.Val { if (this.propName == "font-size") { - const val = CssParser.evaluateCSSToCSS(this.context, expr, this.propName); + const val = evaluateCSSToCSS(this.context, expr, this.propName); return val.visit(this); } return expr; @@ -2868,7 +2868,7 @@ export class CascadeInstance { this.applyVarFilter([this.currentStyle], styler, element); // Calculate calc() - this.applyCalcFilter(this.currentStyle, styler); + this.applyCalcFilter(this.currentStyle, this.context); this.applyAttrFilter(element); const quotesCasc = baseStyle["quotes"] as CascadeValue; @@ -3012,7 +3012,7 @@ export class CascadeInstance { // all variables substituted const validatorSet = (styler as any) .validatorSet as CssValidator.ValidatorSet; - const shorthand = validatorSet.shorthands[name]?.clone(); + const shorthand = validatorSet?.shorthands[name]?.clone(); if (shorthand) { if (Css.isDefaultingValue(value)) { for (const nameLH of shorthand.propList) { @@ -3056,16 +3056,13 @@ export class CascadeInstance { /** * Calculate all calc() in property values in elementStyle */ - applyCalcFilter( - elementStyle: ElementStyle, - styler: CssStyler.AbstractStyler, - ): void { - const visitor = new CalcFilterVisitor(styler); + applyCalcFilter(elementStyle: ElementStyle, context: Exprs.Context): void { + const visitor = new CalcFilterVisitor(context); for (const name in elementStyle) { if (isMapName(name)) { const pseudoMap = getStyleMap(elementStyle, name); for (const pseudoName in pseudoMap) { - this.applyCalcFilter(pseudoMap[pseudoName], styler); + this.applyCalcFilter(pseudoMap[pseudoName], context); } } else if (isPropName(name) && !Css.isCustomPropName(name)) { const cascVal = getProp(elementStyle, name); @@ -4168,7 +4165,11 @@ export class VarFilterVisitor extends Css.FilterVisitor { * Convert calc() to its value */ export class CalcFilterVisitor extends Css.FilterVisitor { - constructor(public styler: CssStyler.AbstractStyler) { + constructor( + public context: Exprs.Context, + public resolveViewportUnit?: boolean, + public percentRef?: number, + ) { super(); } @@ -4179,22 +4180,56 @@ export class CalcFilterVisitor extends Css.FilterVisitor { return value; } const exprText = value.toString().replace(/^calc\b/, "-epubx-expr"); - const unitInCalc = exprText.replace(/^.*?\d([a-zA-Z]+\b|%).*$|^.*$/, "$1"); + if (/\d(%|em|ex|cap|ch|ic|lh|p?v[whbi]|p?vmin|p?vmax)\W/i.test(exprText)) { + return value; + } const exprVal = CssParser.parseValue( - (this.styler as any).scope, + null, new CssTokenizer.Tokenizer(exprText, null), "", ); if (exprVal instanceof Css.Expr) { - const exprResult = exprVal.expr.evaluate((this.styler as any).context); + const exprResult = exprVal.expr.evaluate(this.context); if (typeof exprResult === "number") { - if (unitInCalc && unitInCalc !== "%") { - value = new Css.Numeric(exprResult, "px"); - } - // TODO: percentage value, etc. - // FIXME: font-size-relative or viewport relative unit may be incorrect + value = new Css.Numeric(exprResult, "px"); } } return value; } + + override visitNumeric(numeric: Css.Numeric): Css.Val { + if ( + this.resolveViewportUnit && + Exprs.isViewportRelativeLengthUnit(numeric.unit) + ) { + return new Css.Numeric( + numeric.num * this.context.queryUnitSize(numeric.unit, false), + "px", + ); + } + if (typeof this.percentRef === "number" && numeric.unit === "%") { + return new Css.Numeric((numeric.num * this.percentRef) / 100, "px"); + } + return numeric; + } +} + +export function evaluateCSSToCSS( + context: Exprs.Context, + val: Css.Val, + propName?: string, + percentRef?: number, +): Css.Val { + try { + if (val instanceof Css.Expr) { + return CssParser.evaluateExprToCSS(context, val.expr, propName); + } + if (val instanceof Css.Numeric || val instanceof Css.Func) { + return val.visit(new CalcFilterVisitor(context, true, percentRef)); + } + } catch (err) { + Logging.logger.warn(err); + return Css.empty; + } + return val; } diff --git a/packages/core/src/vivliostyle/css-page.ts b/packages/core/src/vivliostyle/css-page.ts index 637f3df31..d4f40d7b4 100644 --- a/packages/core/src/vivliostyle/css-page.ts +++ b/packages/core/src/vivliostyle/css-page.ts @@ -1478,11 +1478,9 @@ class SingleBoxMarginBoxSizingParam implements MarginBoxSizingParam { scope: Exprs.LexicalScope, private readonly clientLayout: Vtree.ClientLayout, ) { - this.hasAutoSize_ = !PageMaster.toExprAuto( - scope, - style[isHorizontal ? "width" : "height"], - new Exprs.Numeric(scope, 0, "px"), - ); + const val = style[isHorizontal ? "width" : "height"]; + this.hasAutoSize_ = + !val || val === Css.ident.auto || Css.isDefaultingValue(val); } /** @override */ diff --git a/packages/core/src/vivliostyle/css-parser.ts b/packages/core/src/vivliostyle/css-parser.ts index f1e874e5e..1a17c7669 100644 --- a/packages/core/src/vivliostyle/css-parser.ts +++ b/packages/core/src/vivliostyle/css-parser.ts @@ -1850,16 +1850,7 @@ export class Parser { tokenizer.consume(); continue; case Action.VAL_NUMERIC: - if (Exprs.isViewportRelativeLengthUnit(token.text)) { - // Treat numeric value with viewport unit as numeric in expr. - valStack.push( - new Css.Expr( - new Exprs.Numeric(handler.getScope(), token.num, token.text), - ), - ); - } else { - valStack.push(new Css.Numeric(token.num, token.text)); - } + valStack.push(new Css.Numeric(token.num, token.text)); tokenizer.consume(); continue; case Action.VAL_STR: @@ -2809,22 +2800,3 @@ export function evaluateExprToCSS( } throw new Error("E_UNEXPECTED"); } - -/** - * @return val - */ -export function evaluateCSSToCSS( - context: Exprs.Context, - val: Css.Val, - propName?: string, -): Css.Val { - if (val.isExpr()) { - try { - return evaluateExprToCSS(context, (val as Css.Expr).expr, propName); - } catch (err) { - Logging.logger.warn(err); - } - return Css.empty; - } - return val; -} diff --git a/packages/core/src/vivliostyle/exprs.ts b/packages/core/src/vivliostyle/exprs.ts index e990a5a11..a72f88d6e 100644 --- a/packages/core/src/vivliostyle/exprs.ts +++ b/packages/core/src/vivliostyle/exprs.ts @@ -531,7 +531,7 @@ export class Context { } queryVal(scope: LexicalScope, key: string): Result | undefined { - const s = this.scopes[scope.scopeKey]; + const s = scope && this.scopes[scope.scopeKey]; return s ? s[key] : undefined; } @@ -609,7 +609,9 @@ export class Val { return result; } result = this.evaluateCore(context); - context.storeVal(this.scope, this.key, result); + if (this.scope) { + context.storeVal(this.scope, this.key, result); + } return result; } diff --git a/packages/core/src/vivliostyle/ops.ts b/packages/core/src/vivliostyle/ops.ts index 17db0d32a..887efb96b 100644 --- a/packages/core/src/vivliostyle/ops.ts +++ b/packages/core/src/vivliostyle/ops.ts @@ -368,7 +368,7 @@ export class StyleInstance this.styler.cascade.applyVarFilter([pageStyle], this.styler, null); // Calculate calc() - this.styler.cascade.applyCalcFilter(pageStyle, this.styler); + this.styler.cascade.applyCalcFilter(pageStyle, this.styler.context); const pageSizeAndBleed = CssPage.evaluatePageSizeAndBleed( CssPage.resolvePageSizeAndBleed(pageStyle), @@ -1787,7 +1787,7 @@ export class StyleInstance this.styler.cascade.applyVarFilter([cascadedPageStyle], this.styler, null); // Calculate calc() - this.styler.cascade.applyCalcFilter(cascadedPageStyle, this.styler); + this.styler.cascade.applyCalcFilter(cascadedPageStyle, this.styler.context); const pageMaster = this.selectPageMaster(cascadedPageStyle); if (!pageMaster) { @@ -1813,6 +1813,7 @@ export class StyleInstance this, ); this.setPageSizeAndBleed(evaluatedPageSizeAndBleed, page); + CssPage.addPrinterMarks( cascadedPageStyle, evaluatedPageSizeAndBleed, diff --git a/packages/core/src/vivliostyle/page-master.ts b/packages/core/src/vivliostyle/page-master.ts index ce81a5ed4..9209dff22 100644 --- a/packages/core/src/vivliostyle/page-master.ts +++ b/packages/core/src/vivliostyle/page-master.ts @@ -991,7 +991,7 @@ export class PageBoxInstance

> { } } if (val) { - val = CssParser.evaluateCSSToCSS(context, val, name); + val = CssCascade.evaluateCSSToCSS(context, val, name); } return val; } @@ -999,7 +999,10 @@ export class PageBoxInstance

> { getPropAsNumber(context: Exprs.Context, name: string): number { let val = this.style[name]; if (val) { - val = CssParser.evaluateCSSToCSS(context, val, name); + let percentRef = /\b(height|top|bottom)\b/.test(name) + ? context.pageAreaHeight ?? context.pageHeight() + : context.pageAreaWidth ?? context.pageWidth(); + val = CssCascade.evaluateCSSToCSS(context, val, name, percentRef); } return Css.toNumber(val, context); } diff --git a/packages/core/src/vivliostyle/types.ts b/packages/core/src/vivliostyle/types.ts index 37b72c962..32547d2a7 100644 --- a/packages/core/src/vivliostyle/types.ts +++ b/packages/core/src/vivliostyle/types.ts @@ -1249,7 +1249,7 @@ export namespace Vtree { readonly parentShadow: ShadowContext; subShadow: ShadowContext; readonly type: Vtree.ShadowType; - readonly styler: object; + readonly styler: CssStyler.AbstractStyler; equals(other: ShadowContext): boolean; } diff --git a/packages/core/src/vivliostyle/vgen.ts b/packages/core/src/vivliostyle/vgen.ts index 66031ff4e..3b214a166 100644 --- a/packages/core/src/vivliostyle/vgen.ts +++ b/packages/core/src/vivliostyle/vgen.ts @@ -666,7 +666,7 @@ export class ViewFactory // Figure out element's styles let element = this.sourceNode as Element; const styler = this.nodeContext.shadowContext - ? (this.nodeContext.shadowContext.styler as CssStyler.AbstractStyler) + ? this.nodeContext.shadowContext.styler : this.styler; let elementStyle = styler.getStyle(element, false); if (!this.nodeContext.shadowContext) {