that
+ * stands in for it records its own when it opens
*
- * @param array $properties
+ * @param array $computed
*/
- private function setFixedPosBlockComputed(array $properties)
+ private function setFixedPosBlockComputed(array $computed)
{
if ($this->fixedPosBlockElements !== null) {
- $this->fixedPosBlockElements[count($this->fixedPosBlockElements) - 1]['computed'] = $properties;
+ $block = &$this->fixedPosBlockElements[count($this->fixedPosBlockElements) - 1];
+ $block['computed'] = $computed;
+ $block['href'] = '';
+ $block['decorations'] = [];
}
}
@@ -305,6 +415,7 @@ private function newOpenElementStack()
{
$document = $this->newElementFrame('', [], $this->documentLang, 1, 1, '');
$document['computed'] = $this->documentComputed;
+ $document['decorations'] = $this->documentDecorations;
return [$document];
}
@@ -338,6 +449,8 @@ private function newElementFrame($tag, array $attr, $lang, $nthChild, $nthOfType
'childTypeTotals' => null,
'empty' => null,
'computed' => null,
+ 'href' => '',
+ 'decorations' => [],
];
return isset($this->lookAhead[$key]) ? $this->lookAhead[$key] + $frame : $frame;
@@ -362,12 +475,18 @@ private function startElement($tag, array $attr, $selfClosing, $computed = null)
$parent = count($this->openElements) - 1;
if (self::impliesTbody($tag, $this->openElements[$parent])) {
- $this->startElement('TBODY', [], false);
+ $this->startElement('TBODY', [], false, $this->impliedTbodyComputed);
+ $this->impliedTbodyComputed = null;
$parent++;
}
$frame = $this->newChildFrame($this->openElements[$parent], $tag, $attr);
$frame['computed'] = $computed;
+ // Only the content of a block or an inline element reads it: a table's content starts from a text state of its
+ // own, and a void element has none
+ if ($this->cssMode === CssMode::STANDARD && !isset(self::$tableParts[$tag]) && !isset(self::$voidTags[$tag])) {
+ $this->recordDrawnState($frame);
+ }
if ($selfClosing || isset(self::$voidTags[$tag])) {
$this->recordClosedElement($frame);
diff --git a/tests/Mpdf/ComputedValueInheritanceTest.php b/tests/Mpdf/ComputedValueInheritanceTest.php
new file mode 100644
index 000000000..c856398cf
--- /dev/null
+++ b/tests/Mpdf/ComputedValueInheritanceTest.php
@@ -0,0 +1,340 @@
+drawDocument($this->inContext($context, $html), ['cssMode' => $mode]);
+ $mpdf->Close();
+
+ $sizes = $this->keyedByText($mpdf, $mpdf->drawnFontSize);
+ foreach ($expected as $text => $size) {
+ $this->assertArrayHasKey($text, $sizes, $text . ' is drawn');
+ $this->assertEqualsWithDelta($size, $sizes[$text], 0.001, $text);
+ }
+ $this->assertDrawnInContext($context, $mpdf, array_keys($expected));
+ }
+
+ /**
+ * Chains of em and % font sizes, three deep, through blocks, inline elements, a table, its row group and row, and a
+ * table nested in a cell, each in every context under both modes. Legacy does not read a row's or row group's
+ * size, and starts a table from the document's size
+ *
+ * @return array[]
+ */
+ public function fontSizeChains()
+ {
+ $chains = [
+ 'blocks and inline elements' => [
+ '
',
+ ['aa' => 15, 'bb' => 30, 'cc' => 45],
+ ['aa' => 15, 'bb' => 30, 'cc' => 45],
+ ],
+ 'a table in a block' => [
+ '
',
+ ['aa' => 15, 'bb' => 30],
+ ['aa' => 8.25, 'bb' => 16.5],
+ ],
+ 'a row group and a row' => [
+ '
',
+ ['aa' => 30, 'bb' => 15],
+ ['aa' => 40, 'bb' => 20],
+ ],
+ 'a table nested in a cell' => [
+ '
',
+ ['aa' => 15, 'bb' => 30],
+ ['aa' => 8.25, 'bb' => 16.5],
+ ],
+ ];
+
+ $data = [];
+ foreach (['flow', 'table cell', 'header', 'footer', 'positioned block', 'kept block', 'forced page break'] as $context) {
+ foreach ($chains as $name => $chain) {
+ // A table in a table cell is a nested table, whose own cases are above
+ if ($context === 'table cell' && $name !== 'blocks and inline elements') {
+ continue;
+ }
+ $data[CssMode::STANDARD . ': ' . $name . ' in ' . $context] = [CssMode::STANDARD, $context, $chain[0], $chain[1]];
+ $data[CssMode::LEGACY . ': ' . $name . ' in ' . $context] = [CssMode::LEGACY, $context, $chain[0], $chain[2]];
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * A spacing or indent given in em on an ancestor is handed on as the length it came to at the ancestor's size,
+ * so the descendant is drawn as if it had set that length itself. Under legacy the descendant reads the em again
+ * at its own size
+ *
+ * @dataProvider emLengths
+ *
+ * @param string $mode
+ * @param string $html With {A} for the ancestor's declaration and {D} for the descendant's
+ * @param string $declaration In em, on the ancestor, whose font size is 20pt
+ * @param string $computed The same length at 20pt, which the descendant should be drawn with
+ * @param string $reread The same length at the descendant's 10pt
+ */
+ public function testAnEmLengthIsInheritedAsTheLengthItCameTo($mode, $html, $declaration, $computed, $reread)
+ {
+ // An indent moves where the line starts, and spacing makes it wider
+ $measure = strpos($declaration, 'text-indent') === 0 ? 0 : 1;
+ $inherited = $this->firstLine($mode, strtr($html, ['{A}' => $declaration, '{D}' => '']))[$measure];
+ $expected = $this->firstLine($mode, strtr($html, ['{A}' => '', '{D}' => $mode === CssMode::STANDARD ? $computed : $reread]))[$measure];
+ $none = $this->firstLine($mode, strtr($html, ['{A}' => '', '{D}' => '']))[$measure];
+
+ $this->assertNotEquals($none, $expected, 'The length does not change how the line is drawn');
+ $this->assertEqualsWithDelta($expected, $inherited, 0.002);
+ }
+
+ /**
+ * Letter spacing, word spacing and text indent in em, from a block, a list, a cell, a positioned block and a block
+ * laid out again after it is kept together, under both modes
+ *
+ * @return array[]
+ */
+ public function emLengths()
+ {
+ $lengths = [
+ 'letter-spacing' => ['letter-spacing: 0.1em', 'letter-spacing: 2pt', 'letter-spacing: 1pt'],
+ 'word-spacing' => ['word-spacing: 1em', 'word-spacing: 20pt', 'word-spacing: 10pt'],
+ // mPDF has always handed a block's text indent on as the length it came to
+ 'text-indent' => ['text-indent: 2em', 'text-indent: 40pt', 'text-indent: 40pt'],
+ ];
+ $contexts = [
+ 'block to block' => '
',
+ 'block to inline' => '
zz qq qq
',
+ 'inline to inline' => '
zz yy qq qq
',
+ 'list to item' => '
',
+ 'cell to block' => '
',
+ 'positioned block to block' => '
',
+ 'block to table cell' => '
',
+ ];
+
+ $data = [];
+ foreach ([CssMode::STANDARD, CssMode::LEGACY] as $mode) {
+ foreach ($contexts as $context => $html) {
+ foreach ($lengths as $property => $values) {
+ // mPDF lays out a block in a cell as the cell's own content, which is not indented, and an inline
+ // element's text is indented by the block its line is in
+ if ($property === 'text-indent' && (strpos($html, '
qq qq
';
+
+ // Where the line starts
+ $inherited = $this->firstLine($mode, strtr($html, ['{A}' => '1em', '{D}' => 'inherit']))[0];
+ $computed = $this->firstLine($mode, strtr($html, ['{A}' => '1em', '{D}' => '20pt']))[0];
+ $dropped = $this->firstLine($mode, strtr($html, ['{A}' => '1em', '{D}' => '0']))[0];
+
+ $this->assertNotEquals($computed, $dropped);
+ $this->assertEquals($mode === CssMode::STANDARD ? $computed : $dropped, $inherited);
+ }
+
+ /**
+ * The content of a positioned block draws line-height: normal as a paragraph outside one does. Legacy draws it at
+ * the fixed 1.33 the block's channel handed on
+ *
+ * @dataProvider modes
+ *
+ * @param string $mode
+ */
+ public function testThePositionedBlocksContentDrawsANormalLineHeight($mode)
+ {
+ $positioned = $this->lineGap($mode, '');
+ $flow = $this->lineGap($mode, 'qq qq ww ww
');
+ $fixed = $this->lineGap($mode, 'qq qq ww ww
');
+
+ $this->assertNotEquals($flow, $fixed, 'normal is not drawn at 1.33 outside a positioned block');
+ $this->assertEquals($mode === CssMode::STANDARD ? $flow : $fixed, $positioned);
+ }
+
+ /**
+ * A block ends the paragraph an inline element's bidirectional embedding is in. The embedding is opened again for
+ * the inline element's text after the block, which legacy draws outside it
+ *
+ * @dataProvider modes
+ *
+ * @param string $mode
+ */
+ public function testABidiEmbeddingIsOpenedAgainAfterABlock($mode)
+ {
+ $hebrew = html_entity_decode('אב', ENT_QUOTES, 'UTF-8');
+ $mpdf = new TextRecordingMpdf(['mode' => 'utf-8', 'default_font' => 'dejavusans', 'cssMode' => $mode]);
+ $mpdf->WriteHTML('' . $hebrew . ' cdqq
' . $hebrew . ' ef zz
');
+ $mpdf->Close();
+
+ $drawn = array_map('trim', $mpdf->drawnText);
+ $reversed = html_entity_decode('בא', ENT_QUOTES, 'UTF-8');
+ $this->assertContains('cd ' . $reversed, $drawn, 'Before the block');
+ $this->assertContains($mode === CssMode::STANDARD ? 'ef ' . $reversed : $reversed . ' ef', $drawn, 'After the block');
+ }
+
+ /**
+ * The text before a block opened in an inline element is drawn in its own style, not the inline element's: neither
+ * italic nor shadowed. The inline element's text before, in and after the block is. Legacy draws that text in the
+ * shadow of the inline element, and the block and the text after it in neither
+ *
+ * @dataProvider modes
+ *
+ * @param string $mode
+ */
+ public function testTheLineBeforeABlockIsDrawnInItsOwnState($mode)
+ {
+ $mpdf = $this->drawDocument('', ['cssMode' => $mode]);
+ $mpdf->Close();
+
+ $styles = $this->keyedByText($mpdf, $mpdf->drawnFontStyles);
+ $shadowed = array_map(function ($shadow) {
+ return $shadow !== '';
+ }, $this->keyedByText($mpdf, $mpdf->drawnShadows));
+
+ if ($mode === CssMode::STANDARD) {
+ $this->assertSame(['ww' => '', 'xx' => 'I', 'qq' => 'I', 'yy' => 'I', 'zz' => ''], $styles);
+ $this->assertSame(['ww' => false, 'xx' => true, 'qq' => true, 'yy' => true, 'zz' => false], $shadowed);
+ } else {
+ $this->assertSame(['ww' => '', 'xx' => 'I', 'qq' => '', 'yy' => '', 'zz' => ''], $styles);
+ $this->assertSame(['ww' => true, 'xx' => true, 'qq' => false, 'yy' => false, 'zz' => false], $shadowed);
+ }
+ }
+
+ /**
+ * The text before a block opened in a spaced inline element is drawn at its own letter and word spacing, as it is
+ * where the inline element holds no block. Legacy draws it at the inline element's
+ *
+ * @dataProvider modes
+ *
+ * @param string $mode
+ */
+ public function testTheLineBeforeABlockIsDrawnAtItsOwnSpacing($mode)
+ {
+ $boxes = [];
+ foreach (['' => '
', '' => ''] as $open => $close) {
+ $mpdf = $this->drawDocument('ww w xx' . $open . 'qq' . $close . 'yy zz
', ['cssMode' => $mode]);
+ $mpdf->Close();
+ $boxes[] = $this->keyedByText($mpdf, $mpdf->drawnBoxes)['ww w'];
+ }
+
+ if ($mode === CssMode::STANDARD) {
+ $this->assertSame($boxes[1], $boxes[0]);
+ } else {
+ $this->assertNotSame($boxes[1], $boxes[0]);
+ }
+ }
+
+ /**
+ * A table opened in an inline element takes its style, and the inline element's text after the table is drawn in
+ * it again. Legacy draws the table and the text after it in neither, and the text before the span in its colour
+ *
+ * @dataProvider modes
+ *
+ * @param string $mode
+ */
+ public function testTheTextAfterATableInAnInlineElementIsDrawnInItsStyle($mode)
+ {
+ $mpdf = $this->drawDocument('', ['cssMode' => $mode]);
+ $mpdf->Close();
+
+ $styles = $this->keyedByText($mpdf, $mpdf->drawnFontStyles);
+ $colours = $this->keyedByText($mpdf, $mpdf->drawnColours);
+ $green = '0.000 1.000 0.000 rg';
+ $black = '0.000 g';
+
+ if ($mode === CssMode::STANDARD) {
+ $this->assertSame(['ww' => '', 'xx' => 'I', 'qq' => 'I', 'yy' => 'I', 'zz' => ''], $styles);
+ $this->assertSame(['ww' => $black, 'xx' => $green, 'qq' => $green, 'yy' => $green, 'zz' => $black], $colours);
+ } else {
+ $this->assertSame(['ww' => '', 'xx' => 'I', 'qq' => '', 'yy' => '', 'zz' => ''], $styles);
+ $this->assertSame(['ww' => $green, 'xx' => $green, 'qq' => $black, 'yy' => $black, 'zz' => $black], $colours);
+ }
+ }
+
+ /**
+ * How the first line of a document, "qq qq", is drawn: where it starts and how wide it is
+ *
+ * @param string $mode
+ * @param string $html
+ *
+ * @return float[]
+ */
+ private function firstLine($mode, $html)
+ {
+ $mpdf = $this->drawDocument($html, ['cssMode' => $mode]);
+ $mpdf->Close();
+
+ $boxes = $this->keyedByText($mpdf, $mpdf->drawnBoxes);
+ $this->assertArrayHasKey('qq qq', $boxes);
+
+ return [round($boxes['qq qq'][1], 3), round($boxes['qq qq'][2] - $boxes['qq qq'][1], 3)];
+ }
+
+ /**
+ * @param string $mode
+ * @param string $html Two lines, "qq qq" and "ww ww"
+ *
+ * @return float The distance between the tops of the two lines
+ */
+ private function lineGap($mode, $html)
+ {
+ $mpdf = $this->drawDocument($html, ['cssMode' => $mode]);
+ $mpdf->Close();
+
+ $tops = $this->keyedByText($mpdf, $mpdf->drawnY);
+
+ return round($tops['ww ww'] - $tops['qq qq'], 3);
+ }
+}
diff --git a/tests/Mpdf/Css/TextSpacingTest.php b/tests/Mpdf/Css/TextSpacingTest.php
new file mode 100644
index 000000000..76fe51ab9
--- /dev/null
+++ b/tests/Mpdf/Css/TextSpacingTest.php
@@ -0,0 +1,63 @@
+mpdf = new Mpdf(['mode' => 'c']);
+ $this->mpdf->FontSize = 10;
+ $this->sizeConverter = new SizeConverter(96, 11, $this->mpdf, new NullLogger());
+ }
+
+ /**
+ * A spacing is the length it comes to at the font size, and normal or none is what stands for none
+ */
+ public function testLength()
+ {
+ $this->assertSame(10.0, TextSpacing::length($this->sizeConverter, '1em', 10, false));
+ $this->assertSame(2.0, TextSpacing::length($this->sizeConverter, '2mm', 10, 0));
+ $this->assertSame(0.0, TextSpacing::length($this->sizeConverter, '0', 10, false));
+ $this->assertFalse(TextSpacing::length($this->sizeConverter, 'normal', 10, false));
+ $this->assertFalse(TextSpacing::length($this->sizeConverter, '', 10, false));
+ $this->assertSame(0, TextSpacing::length($this->sizeConverter, 'NORMAL', 10, 0));
+ }
+
+ /**
+ * The text state keeps each spacing as given and as its length at the current font size
+ */
+ public function testSet()
+ {
+ TextSpacing::set($this->mpdf, $this->sizeConverter, '0.5em', '2mm');
+
+ $this->assertSame('0.5em', $this->mpdf->lSpacingCSS);
+ $this->assertSame('2mm', $this->mpdf->wSpacingCSS);
+ $this->assertSame(5.0, $this->mpdf->fixedlSpacing);
+ $this->assertSame(2.0, $this->mpdf->minwSpacing);
+
+ TextSpacing::set($this->mpdf, $this->sizeConverter, 'normal', '');
+
+ $this->assertFalse($this->mpdf->fixedlSpacing);
+ $this->assertSame(0, $this->mpdf->minwSpacing);
+ }
+}
diff --git a/tests/Mpdf/CssWideKeywordContextTest.php b/tests/Mpdf/CssWideKeywordContextTest.php
index 93ca2b0f0..831444afc 100644
--- a/tests/Mpdf/CssWideKeywordContextTest.php
+++ b/tests/Mpdf/CssWideKeywordContextTest.php
@@ -98,14 +98,6 @@ public function testEachContextDrawsItsSubject($mode)
}
}
- /**
- * @return string[][] Each CSS mode
- */
- public function modes()
- {
- return [CssMode::STANDARD => [CssMode::STANDARD], CssMode::LEGACY => [CssMode::LEGACY]];
- }
-
/**
* Every keyword, for each property, in every context, under each mode
*
diff --git a/tests/Mpdf/DrawnStyles.php b/tests/Mpdf/DrawnStyles.php
index b7e1c0503..14e8079a1 100644
--- a/tests/Mpdf/DrawnStyles.php
+++ b/tests/Mpdf/DrawnStyles.php
@@ -167,6 +167,16 @@ private function bothModes(array $rows)
return $cases;
}
+ /**
+ * Each CSS mode, for a test that expects the same of both
+ *
+ * @return array[] Each mode, keyed by its name
+ */
+ public function modes()
+ {
+ return [CssMode::STANDARD => [CssMode::STANDARD], CssMode::LEGACY => [CssMode::LEGACY]];
+ }
+
/**
* Keys what was recorded of each piece of text a document drew by the text itself, so a test can look a piece up
* by what it says
diff --git a/tests/Mpdf/InheritanceMatrixTest.php b/tests/Mpdf/InheritanceMatrixTest.php
new file mode 100644
index 000000000..e2ccdad08
--- /dev/null
+++ b/tests/Mpdf/InheritanceMatrixTest.php
@@ -0,0 +1,255 @@
+ 'color: #ff0000',
+ 'font-size' => 'font-size: 16pt',
+ 'font-family' => 'font-family: monospace',
+ 'font-weight' => 'font-weight: bold',
+ 'font-style' => 'font-style: italic',
+ 'letter-spacing' => 'letter-spacing: 1mm',
+ 'word-spacing' => 'word-spacing: 5mm',
+ 'text-transform' => 'text-transform: uppercase',
+ 'text-shadow' => 'text-shadow: 1px 1px #0000ff',
+ 'text-align' => 'text-align: right',
+ 'line-height' => 'line-height: 3',
+ ];
+
+ /**
+ * Each relationship: the document with {A} for the ancestor's declaration, and the same document with {D} where the
+ * descendant sets it itself. The descendant's text is two lines, "qq qq" and "ww ww". A property that lays out
+ * lines is set, as {B}, on what the descendant's lines are laid out in where that is not the descendant: the block
+ * around an inline element, and the cell around a block, which mPDF lays out as the cell's own content
+ */
+ const RELATIONSHIPS = [
+ 'block to block' => [
+ '',
+ '',
+ ],
+ 'block to inline' => [
+ 'zz qq qq ww ww
',
+ 'zz qq qq ww ww
',
+ ],
+ 'inline to inline' => [
+ 'zz yy qq qq ww ww
',
+ 'zz yy qq qq ww ww
',
+ ],
+ 'inline to block' => [
+ 'zzqq qq ww ww
',
+ 'zzqq qq ww ww
',
+ ],
+ 'table to cell' => [
+ '',
+ '',
+ ],
+ 'tbody to cell' => [
+ '',
+ '',
+ ],
+ 'thead to th' => [
+ '',
+ '',
+ ],
+ 'tr to cell' => [
+ '',
+ '',
+ ],
+ 'cell to block' => [
+ '',
+ '',
+ ],
+ 'cell to inline' => [
+ '',
+ '',
+ ],
+ 'outer cell to nested table cell' => [
+ '',
+ '',
+ ],
+ 'block to table cell' => [
+ '',
+ '',
+ ],
+ 'list to item' => [
+ '',
+ '',
+ ],
+ 'item to block' => [
+ '',
+ '',
+ ],
+ 'positioned block to block' => [
+ '',
+ '',
+ ],
+ 'body to block' => [
+ 'qq qq ww ww
',
+ 'qq qq ww ww
',
+ ],
+ ];
+
+ /**
+ * The properties laid out by the block, which an inline element cannot set itself
+ */
+ const BLOCK_PROPERTIES = ['text-align', 'line-height'];
+
+ /**
+ * What legacy hands on, as #547 measured it at d660a410: the properties each relationship drops
+ */
+ const LEGACY_DROPPED = [
+ 'block to block' => ['text-shadow'],
+ 'block to inline' => [],
+ 'inline to inline' => [],
+ 'inline to block' => [
+ 'color', 'font-size', 'font-family', 'font-weight', 'font-style', 'letter-spacing', 'word-spacing',
+ 'text-transform', 'text-shadow', 'text-align', 'line-height',
+ ],
+ 'table to cell' => ['text-transform', 'text-shadow'],
+ 'tbody to cell' => [
+ 'color', 'font-size', 'font-family', 'font-weight', 'font-style', 'letter-spacing', 'word-spacing',
+ 'text-transform', 'text-shadow', 'text-align', 'line-height',
+ ],
+ 'thead to th' => [
+ 'color', 'font-size', 'font-family', 'font-style', 'letter-spacing', 'word-spacing', 'text-transform',
+ 'text-shadow', 'text-align', 'line-height',
+ ],
+ 'tr to cell' => [
+ 'color', 'font-size', 'font-family', 'font-weight', 'font-style', 'letter-spacing', 'word-spacing',
+ 'text-transform', 'text-shadow', 'text-align', 'line-height',
+ ],
+ 'cell to block' => [],
+ 'cell to inline' => [],
+ 'outer cell to nested table cell' => [
+ 'font-size', 'font-family', 'font-weight', 'font-style', 'text-transform', 'line-height',
+ ],
+ 'block to table cell' => [
+ 'color', 'font-size', 'font-family', 'font-weight', 'font-style', 'letter-spacing', 'word-spacing',
+ 'text-transform', 'text-shadow', 'text-align', 'line-height',
+ ],
+ 'list to item' => ['text-shadow'],
+ 'item to block' => ['text-shadow'],
+ 'positioned block to block' => ['word-spacing', 'text-shadow'],
+ 'body to block' => ['text-shadow'],
+ ];
+
+ /**
+ * The descendant's text is drawn with the value the ancestor sets as it is with the value set on the descendant,
+ * except, under legacy, where the relationship drops it
+ *
+ * @dataProvider cells
+ *
+ * @param string $mode
+ * @param string $relationship
+ * @param string $property
+ */
+ public function testTheDescendantIsDrawnWithTheAncestorsValue($mode, $relationship, $property)
+ {
+ list($ancestorHtml, $ownHtml) = self::RELATIONSHIPS[$relationship];
+ $value = self::VALUES[$property];
+ $onBlock = in_array($property, self::BLOCK_PROPERTIES, true);
+
+ $inherited = $this->drawn($mode, strtr($ancestorHtml, ['{A}' => $value]), $property);
+ $none = $this->drawn($mode, strtr($ancestorHtml, ['{A}' => '']), $property);
+ // Set on the block the descendant's lines are laid out in, where that is not the descendant
+ $onBlock = $onBlock && strpos($ownHtml, '{B}') !== false;
+ $own = $this->drawn($mode, strtr($ownHtml, ['{D}' => $onBlock ? '' : $value, '{B}' => $onBlock ? $value : '']), $property);
+
+ // A th is bold whatever its row group sets
+ if ($relationship !== 'thead to th' || $property !== 'font-weight') {
+ $this->assertNotEquals($none, $own, 'The value does not change how the descendant is drawn');
+ }
+
+ if ($mode === CssMode::LEGACY && in_array($property, self::LEGACY_DROPPED[$relationship], true)) {
+ $this->assertNotEquals($own, $inherited);
+ } else {
+ $this->assertEquals($own, $inherited);
+ }
+ }
+
+ /**
+ * Every relationship and property, under both modes. An inline element's own text is not laid out by its
+ * alignment or line height, so those are left out of inline to inline
+ *
+ * @return array[]
+ */
+ public function cells()
+ {
+ $data = [];
+ foreach ([CssMode::STANDARD, CssMode::LEGACY] as $mode) {
+ foreach (array_keys(self::RELATIONSHIPS) as $relationship) {
+ foreach (array_keys(self::VALUES) as $property) {
+ if ($relationship === 'inline to inline' && in_array($property, self::BLOCK_PROPERTIES, true)) {
+ continue;
+ }
+ $data[$mode . ': ' . $relationship . ': ' . $property] = [$mode, $relationship, $property];
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * What of the descendant's text a property changes: the first line's colour, font, size, style, shadow, drawn text
+ * or width, the left edge of the second line, or the distance between the two lines
+ *
+ * @param string $mode
+ * @param string $html
+ * @param string $property
+ *
+ * @return mixed
+ */
+ private function drawn($mode, $html, $property)
+ {
+ // In core-font mode a nested table's cells lose a font family that is the table's default (#648)
+ $mpdf = $this->drawDocument($html, ['mode' => 'utf-8', 'cssMode' => $mode]);
+ $mpdf->Close();
+
+ // Lowercased, as text-transform draws the lines in capitals
+ $lines = array_change_key_case($this->keyedByText($mpdf, array_keys($mpdf->drawnText)));
+ $this->assertArrayHasKey('qq qq', $lines, 'The first line is drawn');
+ $this->assertArrayHasKey('ww ww', $lines, 'The second line is drawn');
+ $first = $lines['qq qq'];
+ $second = $lines['ww ww'];
+
+ switch ($property) {
+ case 'color':
+ return $mpdf->drawnColours[$first];
+ case 'font-size':
+ return round($mpdf->drawnFontSize[$first], 3);
+ case 'font-family':
+ return $mpdf->drawnFontFamily[$first];
+ case 'font-weight':
+ return strpos($mpdf->drawnFontStyles[$first], 'B') !== false;
+ case 'font-style':
+ return strpos($mpdf->drawnFontStyles[$first], 'I') !== false;
+ case 'letter-spacing':
+ case 'word-spacing':
+ return round($mpdf->drawnBoxes[$first][2] - $mpdf->drawnBoxes[$first][1], 3);
+ case 'text-transform':
+ return $mpdf->drawnText[$first];
+ case 'text-shadow':
+ return $mpdf->drawnShadows[$first];
+ case 'text-align':
+ return round($mpdf->drawnBoxes[$second][1], 3);
+ default:
+ return round($mpdf->drawnY[$second] - $mpdf->drawnY[$first], 3);
+ }
+ }
+}
diff --git a/tests/Mpdf/OpenElementStackTest.php b/tests/Mpdf/OpenElementStackTest.php
index a0742fe8a..82fe3afbc 100644
--- a/tests/Mpdf/OpenElementStackTest.php
+++ b/tests/Mpdf/OpenElementStackTest.php
@@ -2,6 +2,8 @@
namespace Mpdf;
+use Mpdf\Css\TextVars;
+
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
/**
@@ -180,30 +182,76 @@ public function testAFrameDescribesItsElement()
}
/**
- * In the standard CSS mode a frame carries the properties merged for its element, with the CSS-wide keywords
- * resolved, and the document's frame carries those of body. A tbody the HTML leaves out is never styled. The
- * legacy mode keeps none
+ * In the standard CSS mode a frame carries its element's computed values, with the CSS-wide keywords resolved, and
+ * the document's frame carries those of body. A font size and an em length are absolute, so that a descendant
+ * taking them, by inheriting them or through inherit, gets the length. A tbody the HTML leaves out carries what it
+ * inherits from its table. The legacy mode keeps none
*/
- public function testAFrameCarriesThePropertiesMergedForItsElement()
+ public function testAFrameCarriesItsElementsComputedValues()
{
- $html = ''
+ $html = ''
. '';
$mpdf = $this->write($html);
$frames = $this->framesAt($mpdf, 'text');
$this->assertSame('#ff0', $frames[0]['computed']['BACKGROUND-COLOR']);
- $this->assertSame('2mm', $frames[1]['computed']['PADDING-LEFT']);
- $this->assertSame('2mm', $frames[2]['computed']['PADDING-LEFT']);
- $this->assertNull($this->frameAt($mpdf, 'cell', 3)['computed']);
- $this->assertSame('TBODY', $this->frameAt($mpdf, 'cell', 3)['tag']);
+ $this->assertSame('20pt', $frames[1]['computed']['FONT-SIZE']);
+ $this->assertStringEndsWith('mm', $frames[1]['computed']['PADDING-LEFT']);
+ $this->assertEqualsWithDelta(20 / Mpdf::SCALE, (float) $frames[1]['computed']['PADDING-LEFT'], 1e-9);
+ $this->assertStringEndsWith('pt', $frames[2]['computed']['FONT-SIZE']);
+ $this->assertEqualsWithDelta(10, (float) $frames[2]['computed']['FONT-SIZE'], 1e-9);
+ $this->assertSame($frames[1]['computed']['PADDING-LEFT'], $frames[2]['computed']['PADDING-LEFT']);
+ $this->assertSame($frames[1]['computed']['LETTER-SPACING'], $frames[2]['computed']['LETTER-SPACING']);
+
+ $tbody = $this->frameAt($mpdf, 'cell', 3);
+ $this->assertSame('TBODY', $tbody['tag']);
+ $this->assertSame('#00f', $tbody['computed']['COLOR']);
+ $this->assertSame('20pt', $tbody['computed']['FONT-SIZE']);
+ $this->assertArrayNotHasKey('PADDING-LEFT', $tbody['computed']);
$mpdf = $this->mpdfRecording(['cssMode' => CssMode::LEGACY]);
$mpdf->WriteHTML($html, HTMLParserMode::DEFAULT_MODE, true, false);
- foreach ($this->framesAt($mpdf, 'text') as $frame) {
+ foreach (array_merge($this->framesAt($mpdf, 'text'), $this->framesAt($mpdf, 'cell')) as $frame) {
$this->assertNull($frame['computed']);
}
}
+ /**
+ * In the standard CSS mode a frame carries the link and the text decorations its element's content is drawn in:
+ * those of the element itself, and none for a positioned block, which is out of the flow. The legacy mode keeps
+ * none
+ */
+ public function testAFrameCarriesTheLinkAndTheDecorationsOfItsContent()
+ {
+ $html = ' ';
+
+ $mpdf = $this->mpdfRecording();
+ $mpdf->WriteHTML($html);
+ $frames = $this->framesAt($mpdf, 'text');
+ $this->assertSame(TextVars::FD_UNDERLINE, $frames[0]['decorations']['textvar']);
+ $this->assertSame('', $frames[1]['href']);
+ $this->assertSame(TextVars::FD_UNDERLINE, $frames[1]['decorations']['textvar']);
+ $this->assertSame('https://example.com/', $frames[2]['href']);
+ $this->assertSame('https://example.com/', $frames[3]['href']);
+ $this->assertSame(TextVars::FD_UNDERLINE, $frames[3]['decorations']['textvar'] & TextVars::FD_UNDERLINE);
+
+ $positioned = $this->framesAt($mpdf, 'positioned');
+ $this->assertSame('DIV', $positioned[2]['tag']);
+ $this->assertSame('', $positioned[2]['href']);
+ $this->assertSame(0, $positioned[2]['decorations']['textvar']);
+ $this->assertSame('', $positioned[3]['href']);
+
+ $mpdf = $this->mpdfRecording(['cssMode' => CssMode::LEGACY]);
+ $mpdf->WriteHTML($html);
+ foreach (array_merge($this->framesAt($mpdf, 'text'), $this->framesAt($mpdf, 'positioned')) as $frame) {
+ $this->assertSame('', $frame['href']);
+ $this->assertSame([], $frame['decorations']);
+ }
+ }
+
/**
* Tables nested in cells keep their own rows and cells, and the outer cell is back on top after them
*/
diff --git a/tests/Mpdf/ParentRelativeFontValuesTest.php b/tests/Mpdf/ParentRelativeFontValuesTest.php
index 19ee01d9c..7c7a174d8 100644
--- a/tests/Mpdf/ParentRelativeFontValuesTest.php
+++ b/tests/Mpdf/ParentRelativeFontValuesTest.php
@@ -29,13 +29,13 @@ public function testNestedValues($mode, $context, $html, array $expected)
}
/**
- * A text circle reads larger and smaller against the document's size, as a form field does, and in the legacy mode
+ * A text circle reads larger and smaller against its parent's size, as a form field does, and in the legacy mode
* ignores them, keeping the paragraph's size rather than being drawn at 0pt
*/
- public function testATextCircleReadsLargerAgainstTheDocumentSize()
+ public function testATextCircleReadsLargerAgainstItsParentsSize()
{
$html = '
';
- foreach ([CssMode::STANDARD => '13.200', CssMode::LEGACY => '14.000'] as $mode => $size) {
+ foreach ([CssMode::STANDARD => '16.800', CssMode::LEGACY => '14.000'] as $mode => $size) {
$mpdf = new Mpdf(['mode' => 'c', 'cssMode' => $mode]);
$mpdf->SetCompression(false);
$mpdf->WriteHTML($html);
@@ -194,8 +194,8 @@ public function providerStartingValues()
],
'larger and smaller form fields' => [
' bb
',
- // 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.
+
+ Finish Price
+
+ 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.
+
נוח Harbourfrom 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 freefor 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.
+
+
+
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.
+
שלום beforeA 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 spanA 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 spanafter 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()
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.
-
+
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
Fixed One Fixed Two Fixed Three
@@ -673,7 +673,7 @@ public function generatePdf()
-
+
Fixed top-right: lower-roman
Fixed One Fixed Two Fixed Three
@@ -690,7 +690,7 @@ public function generatePdf()
-
+
Fixed bottom: hebrew
Fixed One Fixed Two Fixed Three
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.
-
+
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('
');
// 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
');
// A rotated positioned block
- $this->mpdf->WriteHTML('
'
+ $this->mpdf->WriteHTML('
');
// Neighbouring positioned blocks: the card rules stay in the card
- $this->mpdf->WriteHTML('
');
- $this->mpdf->WriteHTML('
'
+ $this->mpdf->WriteHTML('
');
+ $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.
-