diff --git a/CHANGELOG-GRAVITYPDF.md b/CHANGELOG-GRAVITYPDF.md index d4b40a2a9..1957334e4 100644 --- a/CHANGELOG-GRAVITYPDF.md +++ b/CHANGELOG-GRAVITYPDF.md @@ -74,6 +74,10 @@ Read this section before upgrading from upstream mPDF. Each entry says what chan * A block inside an underlined inline element, as in `…
…
…
`, is underlined too, as is the text after it up to the element's end tag. * Floats, positioned blocks and inline blocks no longer take the decorations of the element they are in. Tables never did. * A child block no longer takes its parent's `vertical-align`, which is not inherited. +* **Every element inherits its parent's computed values**, as in a browser, under the standard `cssMode`. The separate ways mPDF handed styles from a block to its children, from an inline element to a block in it, from a table to its cells and from a positioned block to its content each carried their own set of properties and values. They are replaced by one: each open element keeps the computed values of the inherited properties, and its children start from them. Set `'cssMode' => \Mpdf\CssMode::LEGACY` to keep the old output. [#547] [#683] + * A length given in `em`, `ex`, `ch` or `rem` is inherited as the length it came to, not as the unit read again at the child's size. `letter-spacing: 0.1em` on a 20pt block spaces the letters of a 10pt paragraph in it 2pt apart, not 1pt. `inherit` on a length that is not inherited, such as `padding-left: inherit`, takes it in the same way. + * The content of a positioned block draws `line-height: normal` as the rest of the document does. It used to be drawn at a fixed 1.33, which with DejaVu Sans or Times sets the lines a little further apart. To keep the old spacing, add `line-height: 1.33` to the positioned block. + * A bidirectional embedding, as in `…
…
…
`, is opened again for the inline element's text after the block. New features ------------ @@ -278,7 +282,7 @@ Bugfixes ### Forms * A ``, `

', - // From the document's 11pt, as a field's other sizes are - ['aa' => ['', 13.2], 'bb' => ['', 9.17], 'cc' => ['', 13.2]], + // From the paragraph's 14pt, as each font size is computed from the parent's + ['aa' => ['', 16.8], 'bb' => ['', 11.67], 'cc' => ['', 16.8]], // Ignored rather than drawn at 0pt, so each field keeps the paragraph's size, as an ignored size does elsewhere ['aa' => ['', 14.0], 'bb' => ['', 14.0], 'cc' => ['', 14.0]], ], diff --git a/tests/Mpdf/TableInheritsFromBlockTest.php b/tests/Mpdf/TableInheritsFromBlockTest.php index 007ca5ad6..7d6b7e03f 100644 --- a/tests/Mpdf/TableInheritsFromBlockTest.php +++ b/tests/Mpdf/TableInheritsFromBlockTest.php @@ -100,9 +100,8 @@ public function blockPropertiesInContexts() /** * A table in a block that sets line-height: normal, or sets none, is laid out in the standard mode as one whose - * cells set normal, which is drawn as a paragraph's is, from normalLineheight. A positioned block that sets none - * hands its content normal as that number, as it does its paragraphs. In the legacy mode the table keeps its own - * line-height of 1.2 + * cells set normal, which is drawn as a paragraph's is, from normalLineheight. In the legacy mode the table + * keeps its own line-height of 1.2 */ public function testALineHeightOfNormalIsDrawnAsAParagraphsInTheStandardMode() { @@ -111,9 +110,6 @@ public function testALineHeightOfNormalIsDrawnAsAParagraphsInTheStandardMode() foreach (['block', 'list item', 'positioned block', 'cell'] as $context) { $expected = $this->drawnBoxes($mode, $context, '', $lineHeight); $this->assertEquals($expected, $this->drawnBoxes($mode, $context, 'line-height: normal', ''), $mode . ': ' . $context); - if ($mode === CssMode::STANDARD && $context === 'positioned block') { - $expected = $this->drawnBoxes($mode, $context, '', 'line-height: 1.33'); - } $this->assertEquals($expected, $this->drawnBoxes($mode, $context, '', ''), $mode . ': ' . $context . ', setting none'); } } diff --git a/tests/Mpdf/Tag/TFootTest.php b/tests/Mpdf/Tag/TFootTest.php index df8110092..f8c8a722c 100644 --- a/tests/Mpdf/Tag/TFootTest.php +++ b/tests/Mpdf/Tag/TFootTest.php @@ -24,8 +24,12 @@ public function testOpen_TFoot() $this->assertEquals('TFOOT', $this->mpdf->lastoptionaltag); } + /** + * Under the legacy cascade the row group's font weight is kept for its cells, as well as its vertical-align + */ public function testOpen_TFoot_WithInlineStyle() { + $this->mpdf->cssMode = \Mpdf\CssMode::LEGACY; $attr = ['STYLE' => 'font-weight: bold; vertical-align: top;']; $ahtml = []; $ihtml = 0; @@ -38,6 +42,21 @@ public function testOpen_TFoot_WithInlineStyle() $this->assertEquals('top', $this->mpdf->tfoot_valign_default); } + /** + * Under the standard cascade the cells inherit the row group's font weight from its frame, so none is kept for them + */ + public function testOpen_TFoot_KeepsNoFontWeightUnderTheStandardCascade() + { + $attr = ['STYLE' => 'font-weight: bold; vertical-align: top;']; + $ahtml = []; + $ihtml = 0; + + $this->tag->open($attr, $ahtml, $ihtml); + + $this->assertEmpty($this->mpdf->tfoot_font_weight); + $this->assertEquals('top', $this->mpdf->tfoot_valign_default); + } + public function testClose_TFoot() { $ahtml = []; diff --git a/tests/Mpdf/Tag/THeadTest.php b/tests/Mpdf/Tag/THeadTest.php index fde4174fe..68dba1c86 100644 --- a/tests/Mpdf/Tag/THeadTest.php +++ b/tests/Mpdf/Tag/THeadTest.php @@ -24,8 +24,12 @@ public function testOpen_THead() $this->assertEquals('THEAD', $this->mpdf->lastoptionaltag); } + /** + * Under the legacy cascade the row group's font weight is kept for its cells, as well as its vertical-align + */ public function testOpen_THead_WithInlineStyle() { + $this->mpdf->cssMode = \Mpdf\CssMode::LEGACY; $attr = ['STYLE' => 'font-weight: bold; vertical-align: bottom;']; $ahtml = []; $ihtml = 0; @@ -38,6 +42,21 @@ public function testOpen_THead_WithInlineStyle() $this->assertEquals('bottom', $this->mpdf->thead_valign_default); } + /** + * Under the standard cascade the cells inherit the row group's font weight from its frame, so none is kept for them + */ + public function testOpen_THead_KeepsNoFontWeightUnderTheStandardCascade() + { + $attr = ['STYLE' => 'font-weight: bold; vertical-align: bottom;']; + $ahtml = []; + $ihtml = 0; + + $this->tag->open($attr, $ahtml, $ihtml); + + $this->assertEmpty($this->mpdf->thead_font_weight); + $this->assertEquals('bottom', $this->mpdf->thead_valign_default); + } + public function testClose_THead() { $ahtml = []; diff --git a/tests/Mpdf/Tag/TableTest.php b/tests/Mpdf/Tag/TableTest.php index 5071de4bd..9663e6fb1 100644 --- a/tests/Mpdf/Tag/TableTest.php +++ b/tests/Mpdf/Tag/TableTest.php @@ -3,6 +3,7 @@ namespace Mpdf\Tag; use Mpdf\Css\Border; +use Mpdf\CssMode; use Mpdf\Mpdf; class TableTest extends BaseTagTestCase @@ -67,6 +68,9 @@ public function testOpenNestedTable() $this->assertEquals([0, 0, 1], $table['nestedpos']); } + /** + * Under the legacy CSS mode a table hands its cells its text properties through base_table_properties + */ public function testCssProperties() { $attr = [ @@ -75,19 +79,38 @@ public function testCssProperties() $ahtml = []; $ihtml = 0; + $this->mpdf->cssMode = CssMode::LEGACY; $this->tag->open($attr, $ahtml, $ihtml); $table = $this->mpdf->table[$this->mpdf->tableLevel][1]; $this->assertEquals('rtl', $table['direction']); $this->assertEquals('#ff0000', $table['bgcolor'][-1]); $this->assertEquals(Border::ALL, $table['border']); - $this->assertSame('700', $this->mpdf->base_table_properties['FONT-WEIGHT']); // the computed weight + $this->assertEqualsIgnoringCase('BOLD', $this->mpdf->base_table_properties['FONT-WEIGHT']); $this->assertEqualsIgnoringCase('ITALIC', $this->mpdf->base_table_properties['FONT-STYLE']); $this->assertEquals('blue', $this->mpdf->base_table_properties['COLOR']); $this->assertEquals('2px', $this->mpdf->base_table_properties['LETTER-SPACING']); $this->assertEquals('5px', $this->mpdf->base_table_properties['WORD-SPACING']); } + /** + * Under the standard CSS mode a table's cells inherit from its frame, and base_table_properties keeps only the + * font and size a nested table's end puts back + */ + public function testStandardCssPropertiesAreNotHandedOnThroughBaseProperties() + { + $attr = ['STYLE' => 'direction: rtl; font-family: serif; font-size: 14pt; font-weight: bold; color: blue;']; + $ahtml = []; + $ihtml = 0; + + $this->tag->open($attr, $ahtml, $ihtml); + $table = $this->mpdf->table[$this->mpdf->tableLevel][1]; + + $this->assertEquals('rtl', $table['direction']); + $this->assertSame(['FONT-FAMILY', 'FONT-SIZE'], array_keys($this->mpdf->base_table_properties)); + $this->assertEqualsWithDelta(14 / Mpdf::SCALE, (float) $this->mpdf->base_table_properties['FONT-SIZE'], 1e-9); + } + public function testAttributes() { $attr = [ diff --git a/tests/Mpdf/UniversalSelectorTest.php b/tests/Mpdf/UniversalSelectorTest.php index 9cae898bb..3d7c6dab7 100644 --- a/tests/Mpdf/UniversalSelectorTest.php +++ b/tests/Mpdf/UniversalSelectorTest.php @@ -338,8 +338,9 @@ public function testAResetReachesTheDefaultMarginsAndPadding() $this->assertSame($inline->drawnText, $universal->drawnText); $this->assertEquals($inline->drawnBoxes, $universal->drawnBoxes); $this->assertEquals($inline->drawnY, $universal->drawnY); - $this->assertEquals($plain->drawnBoxes, $legacy->drawnBoxes); - $this->assertEquals($plain->drawnY, $legacy->drawnY); + // The standard mode computes the cell's lengths in another order, which can differ in the last bits + $this->assertEqualsWithDelta($plain->drawnBoxes, $legacy->drawnBoxes, 1e-9); + $this->assertEqualsWithDelta($plain->drawnY, $legacy->drawnY, 1e-9); } /** diff --git a/tests/Snapshots/BarcodeSnapshotTest.php b/tests/Snapshots/BarcodeSnapshotTest.php index caa6d2824..6a15d613e 100644 --- a/tests/Snapshots/BarcodeSnapshotTest.php +++ b/tests/Snapshots/BarcodeSnapshotTest.php @@ -442,7 +442,7 @@ public function generatePdf() -
+

(00) 0346987 35346987 355
diff --git a/tests/Snapshots/ComputedValuesBrochureLegacySnapshotTest.php b/tests/Snapshots/ComputedValuesBrochureLegacySnapshotTest.php new file mode 100644 index 000000000..7d431f62c --- /dev/null +++ b/tests/Snapshots/ComputedValuesBrochureLegacySnapshotTest.php @@ -0,0 +1,21 @@ +cascade(); + } + + /** + * The brochure, each part under a caption saying what the standard and the legacy cascade draw + */ + public function generatePdf() + { + ob_start(); + ?> + + +

Launch offer
20% off
until June

+ +

The Harbour chair

+

Written with cssMode set to cascade(); ?>.

+

The red badge, at the foot of the page, is a positioned block with line-height: normal. Standard: its three lines are as far apart as lines of the same size in the flow. Legacy: they are set at a fixed 1.33.

+ +

The lead paragraph sets letter-spacing: 0.08em and word-spacing: 0.3em at 12.6pt, and the small run in it is 60% of that. Standard: the small run is spaced as widely as the paragraph. Legacy: its spacing is read again at its own size, and is narrower.

+

Hand-built in oak, finished in linseed oil and guaranteed for ten years

+ +

Why it lasts

+

Each card has padding: 0.8em at 10.8pt; the note in it is 0.75em and sets padding: inherit. Standard: the note's text is indented as far as the card's. Legacy: the note has no padding of its own.

+
Solid oak frame, jointed without screws. +
Joints are pegged, so the frame can be tightened after years of use.
+
+
Seat woven from paper cord. +
The cord is replaceable, and a kit comes with every chair.
+
+ +

Prices

+

The table is 1.1em of the body's 9pt, its header row group 120% of that, and the small row 80%. Standard: the header is 11.9pt and the small row 7.9pt. Legacy: the header and the small row are 9.9pt, as a row group and a row hand their cells nothing.

+ + + + + + + +
FinishPrice
Natural oak€480
Smoked oak€540
Delivery in the EU€35
+ +

What customers say

+

A right-to-left span wraps the testimonial and a block that credits it. Standard: after the block, as before it, the Hebrew word is to the right of the brand name. Legacy: after the block it is to the left.

+
נוח Harbour
from a customer in Haifa
מומלץ Harbour
+ +

A highlighted run, italic with a gold shadow, is interrupted by a block. Standard: the words before the run are upright and unshadowed. Legacy: they take the run's shadow, and the block and the words after it lose the highlight.

+
Order before June and the delivery is free
for every chair in the range
anywhere in the EU
.
+ mpdf = $this->createMpdf(['cssMode' => $this->cascade()]); + $this->mpdf->WriteHTML($html); + } + +} diff --git a/tests/Snapshots/ComputedValuesBrochureStandardSnapshotTest.php b/tests/Snapshots/ComputedValuesBrochureStandardSnapshotTest.php new file mode 100644 index 000000000..0d887890c --- /dev/null +++ b/tests/Snapshots/ComputedValuesBrochureStandardSnapshotTest.php @@ -0,0 +1,21 @@ + + + +

Descendants inherit computed values

+ +
+

letter-spacing: 0.1em on a 20pt div. The 10pt paragraph inside it has its letters 2pt apart, like the one set to 2pt below it, not 1pt.

+

Spaced by the div's em

+
+

Spaced by 2pt of its own

+ +

word-spacing: 1em on a 20pt paragraph. The words of the 10pt span in it are as far apart as the paragraph's own.

+

Big words and small words in a span

+ +
+

padding-left: 1em on a 20pt div, and padding-left: inherit on the 10pt paragraph in it. The paragraph's text starts two div paddings in, 14mm from the div's border.

+

Padded by what the div's em came to

+
+ +

font-size: 20pt, then 1.5em, 50% and 2em. The words are 30pt, 15pt and 30pt.

+
Thirty fifteen thirty
+ +

A span set to dir="rtl" around a block. Before and after the block, the Hebrew word is to the right of the Latin one. The block inherits the span's direction and is aligned right.

+
שלום before
A block in the span
שלום after
+ +

A block in an italic, shadowed span. The words before the span are upright and unshadowed; the span's words before, in and after the block are italic and shadowed.

+
Upright words in the span
A block in the span
after the block
and upright again
+ +

A table in an italic, green span. The cell and the span's words before and after the table are italic and green; the words outside the span are not.

+
Plain words in the span
A cell in the span
after the table
and plain again
+ +

A positioned block at the foot of the page, and a paragraph beside it in the flow. Both have line-height: normal, and their lines are the same distance apart.

+

A positioned block
at line-height: normal
three lines

+

A paragraph in the flow
at line-height: normal
three lines

+ mpdf = $this->createMpdf(); + $this->mpdf->WriteHTML($html); + } + +} diff --git a/tests/Snapshots/FixedPseudoClassSnapshotTest.php b/tests/Snapshots/FixedPseudoClassSnapshotTest.php index 7e212471b..25c0f7783 100644 --- a/tests/Snapshots/FixedPseudoClassSnapshotTest.php +++ b/tests/Snapshots/FixedPseudoClassSnapshotTest.php @@ -91,7 +91,7 @@ public function generatePdf()
OneTwoThree

In the page header, the link is red. In the positioned box at the foot of the page, :root div.box p makes the text red.

-

Positioned box: red

+

Positioned box: red

DRAFT FOR REVIEW

diff --git a/tests/Snapshots/ListStyleTypeSnapshotTest.php b/tests/Snapshots/ListStyleTypeSnapshotTest.php index 6e953cee2..8197a197d 100644 --- a/tests/Snapshots/ListStyleTypeSnapshotTest.php +++ b/tests/Snapshots/ListStyleTypeSnapshotTest.php @@ -656,7 +656,7 @@ public function generatePdf()

The following lists are rendered inside fixed-position containers.

-
+

Fixed top-left: decimal

  1. Fixed One
  2. Fixed Two
  3. Fixed Three
  4. @@ -673,7 +673,7 @@ public function generatePdf()
-
+

Fixed top-right: lower-roman

  1. Fixed One
  2. Fixed Two
  3. Fixed Three
  4. @@ -690,7 +690,7 @@ public function generatePdf()
-
+

Fixed bottom: hebrew

  1. Fixed One
  2. Fixed Two
  3. Fixed Three
  4. diff --git a/tests/Snapshots/LookAheadSelectorSnapshotTest.php b/tests/Snapshots/LookAheadSelectorSnapshotTest.php index 9cabb9b99..e531672e1 100644 --- a/tests/Snapshots/LookAheadSelectorSnapshotTest.php +++ b/tests/Snapshots/LookAheadSelectorSnapshotTest.php @@ -91,7 +91,7 @@ public function generatePdf()
    • First item
    • Second item
    • Last item

    In the page header, the second line is red. In the positioned box at the foot of the page, its last line is red.

    -

    Positioned box, first line

    Positioned box, last line: red

    +

    Positioned box, first line

    Positioned box, last line: red

    An absolutely positioned block at the foot of the page holds a table whose td:nth-child(2) is purple: Second is purple.

    -
    +
    FirstSecondThird
    diff --git a/tests/Snapshots/PositionedBlockRotateSnapshotTest.php b/tests/Snapshots/PositionedBlockRotateSnapshotTest.php index af14b02f9..050079f3f 100644 --- a/tests/Snapshots/PositionedBlockRotateSnapshotTest.php +++ b/tests/Snapshots/PositionedBlockRotateSnapshotTest.php @@ -37,8 +37,8 @@ public function generatePdf() $html = '

    rotate on a positioned block

    '; diff --git a/tests/Snapshots/PositionedBlockSelectorSnapshotTest.php b/tests/Snapshots/PositionedBlockSelectorSnapshotTest.php index 8541a9c2a..d33d92cbc 100644 --- a/tests/Snapshots/PositionedBlockSelectorSnapshotTest.php +++ b/tests/Snapshots/PositionedBlockSelectorSnapshotTest.php @@ -54,23 +54,23 @@ public function generatePdf() // The same card in normal flow and positioned absolutely beside it $this->mpdf->WriteHTML('
    ' . $card . '
    '); - $this->mpdf->WriteHTML('
    ' . $card . '
    '); + $this->mpdf->WriteHTML('
    ' . $card . '
    '); // An id rule inside a fixed block - $this->mpdf->WriteHTML('
    ' + $this->mpdf->WriteHTML('
    ' . '

    Fixed paragraph

    Fixed cell
    '); // A rule with an element between the block and the paragraph matches only the nested paragraph - $this->mpdf->WriteHTML('
    ' + $this->mpdf->WriteHTML('
    ' . '

    Directly in the box, plain

    Nested in a div, orange

    '); // A rotated positioned block - $this->mpdf->WriteHTML('
    ' + $this->mpdf->WriteHTML('
    ' . '

    Rotated paragraph

    '); // Neighbouring positioned blocks: the card rules stay in the card - $this->mpdf->WriteHTML('

    Card

    Card paragraph

    '); - $this->mpdf->WriteHTML('
    ' + $this->mpdf->WriteHTML('

    Card

    Card paragraph

    '); + $this->mpdf->WriteHTML('
    ' . '

    Plain heading

    Plain paragraph, not a card

    '); } diff --git a/tests/Snapshots/PositionedHtmlSnapshotTest.php b/tests/Snapshots/PositionedHtmlSnapshotTest.php index 0b092b1ff..67119cad0 100644 --- a/tests/Snapshots/PositionedHtmlSnapshotTest.php +++ b/tests/Snapshots/PositionedHtmlSnapshotTest.php @@ -47,7 +47,7 @@ public function generatePdf() } .myfixed1 { - position: absolute; + position: absolute; line-height: 1.33; overflow: visible; left: 0; bottom: 0; @@ -60,7 +60,7 @@ public function generatePdf() } .myfixed2 { - position: fixed; + position: fixed; line-height: 1.33; overflow: auto; right: 0; bottom: 0mm; @@ -129,7 +129,7 @@ public function generatePdf() -
    +

    mPDF

    Fixed-position block element with Autofit

    Using the CSS properties position and overflow:auto it is possible to fit text to a single page:
    diff --git a/tests/Snapshots/TableCellDescendantSelectorSnapshotTest.php b/tests/Snapshots/TableCellDescendantSelectorSnapshotTest.php index 366294bbb..93dd4b6c9 100644 --- a/tests/Snapshots/TableCellDescendantSelectorSnapshotTest.php +++ b/tests/Snapshots/TableCellDescendantSelectorSnapshotTest.php @@ -128,7 +128,7 @@ public function generatePdf()

    A table in a positioned block

    White on teal words from #side td span, in the block positioned at the bottom right of this page.

    -
    +
    Fixed cell text