Skip to content

Commit f114a3f

Browse files
committed
extended HTML/CSS support
1 parent 163444c commit f114a3f

4 files changed

Lines changed: 735 additions & 73 deletions

File tree

examples/E073_css_supported_categories.php

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,283 @@
356356
<input class="ctl-button" type="button" name="demo_btn" value="Action" />
357357
</div>
358358
</div>
359+
360+
<div class="page-break"></div>
361+
<h2>14) Shorthand Normalization Edge Cases</h2>
362+
<p>This section tests shorthand CSS normalization patterns that occur in real-world documents,
363+
ensuring margin, padding, border and background properties are properly expanded to longhands.</p>
364+
<style>
365+
.edge-case { margin: 3mm 0 3mm 0; padding: 2mm; border: 0.3mm solid #333; margin-bottom: 2mm; }
366+
.edge-single { margin: 0; padding: 3pt; border: 0.2mm dotted #666; }
367+
.edge-four-value { margin-top: 2pt; margin-right: 4pt; margin-bottom: 2pt; margin-left: 4pt; }
368+
.edge-border-shorthand { border: 0.5pt solid #999; padding: 4pt; }
369+
.edge-background-single { background: #f0f0f0; padding: 3pt; }
370+
</style>
371+
372+
<div class="edge-case">
373+
<strong>Case 1: Four-value margin</strong>
374+
<code>margin: 3mm 0 3mm 0;</code>
375+
This should expand to T=3mm, R=0, B=3mm, L=0. The box should have vertical margins but no horizontal margins.
376+
</div>
377+
378+
<div class="edge-single">
379+
<strong>Case 2: Single-value shorthand</strong>
380+
<code>margin: 0; padding: 3pt;</code>
381+
All sides should be 0 for margin, 3pt for padding.
382+
</div>
383+
384+
<div class="edge-four-value">
385+
<strong>Case 3: Individual longhand properties</strong>
386+
<code>margin-top: 2pt; margin-right: 4pt; margin-bottom: 2pt; margin-left: 4pt;</code>
387+
Should render with alternating 2pt/4pt margins (simulating different rhythms).
388+
</div>
389+
390+
<div class="edge-border-shorthand">
391+
<strong>Case 4: Border shorthand expansion</strong>
392+
<code>border: 0.5pt solid #999; padding: 4pt;</code>
393+
All four border sides should be solid, 0.5pt width, #999 color. Padding should be 4pt on all sides.
394+
</div>
395+
396+
<div class="edge-background-single">
397+
<strong>Case 5: Single-value background</strong>
398+
<code>background: #f0f0f0;</code>
399+
Background color should be light gray across the entire box.
400+
</div>
401+
402+
<div class="page-break"></div>
403+
<h2>15) Invoice-Style Shorthand Patterns</h2>
404+
<p>This section replicates the CSS patterns from the real-world invoice_statement test case to verify
405+
that all shorthand normalization edge cases render correctly.</p>
406+
<style>
407+
.inv-header { border-bottom: 1pt solid #444; margin-bottom: 8pt; padding-bottom: 4pt; }
408+
.inv-meta { float: right; text-align: right; }
409+
.inv-table { width: 100%; border-collapse: collapse; }
410+
.inv-table th, .inv-table td { border: 0.5pt solid #999; padding: 4pt; }
411+
.inv-total { font-weight: bold; background: #f0f0f0; }
412+
</style>
413+
414+
<div class="inv-header">
415+
<div class="inv-meta">Invoice #INV-2048<br/>Date: 2026-05-10</div>
416+
<h3 style="margin: 0; font-size: 12pt;">Blue Finch Supply</h3>
417+
<p style="margin: 2pt 0 0 0;">Monthly service statement</p>
418+
</div>
419+
420+
<table class="inv-table">
421+
<tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Amount</th></tr>
422+
<tr><td>Monitoring subscription</td><td>1</td><td>49.00</td><td>49.00</td></tr>
423+
<tr><td>Priority support</td><td>2</td><td>15.00</td><td>30.00</td></tr>
424+
<tr class="inv-total"><td colspan="3">Total</td><td>79.00</td></tr>
425+
</table>
426+
427+
<p style="margin-top: 4pt; font-size: 9pt; color: #666;">
428+
Edge cases tested: border-bottom shorthand, margin-bottom, padding-bottom,
429+
border shorthand on table cells, background shorthand on row, and margin values in inline styles.
430+
</p>
431+
432+
<div class="page-break"></div>
433+
<h2>16) Non-Font Shorthand Expansion Checks</h2>
434+
<p>This section isolates non-font shorthand properties that can be normalized conservatively in invoice-like pages.
435+
It helps visually verify border-color/style/width and background shorthands without engine workarounds.</p>
436+
<style>
437+
.nf-grid { border: 0.4pt solid #a8b3be; padding: 5pt; background: #fbfdff; }
438+
.nf-case { margin-bottom: 6pt; padding: 4pt; border: 0.4pt solid #aab5c2; }
439+
.nf-border-color {
440+
border-style: solid;
441+
border-width: 1pt;
442+
border-color: #cc0000 #0077cc #228b22 #a35f00;
443+
}
444+
.nf-border-style {
445+
border-width: 1pt;
446+
border-style: dotted dashed solid double;
447+
border-color: #666;
448+
}
449+
.nf-border-width {
450+
border-style: solid;
451+
border-width: 0.5pt 1.2pt 2pt 3pt;
452+
border-color: #444;
453+
}
454+
.nf-border-mixed {
455+
border-top: 2pt solid #004f9f;
456+
border-right: 1pt dashed #2f8f2f;
457+
border-bottom: 1.5pt dotted #9c3f00;
458+
border-left: 0.7pt solid #555;
459+
background: #f6f8fa;
460+
}
461+
.nf-bg-hex { background: #f0f0f0; }
462+
.nf-bg-name { background: silver; }
463+
</style>
464+
465+
<div class="nf-grid">
466+
<div class="nf-case nf-border-color">
467+
<strong>Case A: border-color 4-values</strong>
468+
<code>border-color: #cc0000 #0077cc #228b22 #a35f00;</code>
469+
Top/right/bottom/left borders should show distinct colors.
470+
</div>
471+
472+
<div class="nf-case nf-border-style">
473+
<strong>Case B: border-style 4-values</strong>
474+
<code>border-style: dotted dashed solid double;</code>
475+
Border pattern should vary by side.
476+
</div>
477+
478+
<div class="nf-case nf-border-width">
479+
<strong>Case C: border-width 4-values</strong>
480+
<code>border-width: 0.5pt 1.2pt 2pt 3pt;</code>
481+
Border thickness should visibly differ by side.
482+
</div>
483+
484+
<div class="nf-case nf-border-mixed">
485+
<strong>Case D: per-side border shorthand</strong>
486+
<code>border-top/right/bottom/left: ...</code>
487+
Each side should preserve its own width/style/color tuple.
488+
</div>
489+
490+
<div class="nf-case nf-bg-hex">
491+
<strong>Case E: background shorthand (hex color)</strong>
492+
<code>background: #f0f0f0;</code>
493+
Block should render with a light gray fill.
494+
</div>
495+
496+
<div class="nf-case nf-bg-name">
497+
<strong>Case F: background shorthand (named color)</strong>
498+
<code>background: silver;</code>
499+
Block should render with named color fill.
500+
</div>
501+
</div>
502+
503+
<div class="page-break"></div>
504+
<h2>17) Non-Font Shorthand Stress Cases</h2>
505+
<p>This section focuses on shorthand combinations that are often normalized conservatively in static PDF renderers.
506+
The primary target here is complex <code>background</code> shorthand token ordering and mixed token sets.</p>
507+
<style>
508+
.nf2-wrap { border: 0.4pt solid #9fb0be; padding: 5pt; background: #fcfdff; }
509+
.nf2-case { margin-bottom: 6pt; padding: 4pt; border: 0.4pt solid #a6b2bf; }
510+
.nf2-bg-order-a { background: #e8f2ff no-repeat left top; }
511+
.nf2-bg-order-b { background: no-repeat right top #ffeedd; }
512+
.nf2-bg-order-c { background: left bottom #eaf9ea no-repeat; }
513+
.nf2-bg-with-image-token { background: #f5eefc url("missing-resource.png") no-repeat center top; }
514+
.nf2-bg-transparent { background: transparent; }
515+
.nf2-margin-pad-a { margin: 2pt 8pt; padding: 1pt 3pt 5pt; }
516+
.nf2-margin-pad-b { margin: 4pt 2pt 1pt; padding: 2pt; }
517+
</style>
518+
519+
<div class="nf2-wrap">
520+
<div class="nf2-case nf2-bg-order-a">
521+
<strong>Case A: background order (color first)</strong>
522+
<code>background: #e8f2ff no-repeat left top;</code>
523+
Expect light blue fill.
524+
</div>
525+
526+
<div class="nf2-case nf2-bg-order-b">
527+
<strong>Case B: background order (color last)</strong>
528+
<code>background: no-repeat right top #ffeedd;</code>
529+
Expect peach fill.
530+
</div>
531+
532+
<div class="nf2-case nf2-bg-order-c">
533+
<strong>Case C: background order (position first)</strong>
534+
<code>background: left bottom #eaf9ea no-repeat;</code>
535+
Expect pale green fill.
536+
</div>
537+
538+
<div class="nf2-case nf2-bg-with-image-token">
539+
<strong>Case D: background with image token</strong>
540+
<code>background: #f5eefc url("missing-resource.png") no-repeat center top;</code>
541+
Even if image token is ignored, fallback lavender color should still fill the block.
542+
</div>
543+
544+
<div class="nf2-case nf2-bg-transparent">
545+
<strong>Case E: transparent background</strong>
546+
<code>background: transparent;</code>
547+
Should not paint an opaque fill.
548+
</div>
549+
550+
<div class="nf2-case nf2-margin-pad-a">
551+
<strong>Case F: mixed margin/padding shorthand (2 + 3 values)</strong>
552+
<code>margin: 2pt 8pt; padding: 1pt 3pt 5pt;</code>
553+
Horizontal margins should be wider than vertical, with asymmetric bottom padding.
554+
</div>
555+
556+
<div class="nf2-case nf2-margin-pad-b">
557+
<strong>Case G: mixed margin/padding shorthand (3 + 1 values)</strong>
558+
<code>margin: 4pt 2pt 1pt; padding: 2pt;</code>
559+
Top margin should be largest; padding should be uniform.
560+
</div>
561+
</div>
562+
563+
<div class="page-break"></div>
564+
<h2>18) Shorthand vs Longhand Override Order</h2>
565+
<p>This section checks cascade order interactions where shorthand and matching longhands appear
566+
in different declaration orders. Later declarations should win.</p>
567+
<style>
568+
.ord-wrap { border: 0.4pt solid #9eacb8; padding: 5pt; background: #fcfdff; }
569+
.ord-case { margin-bottom: 6pt; padding: 4pt; border: 0.4pt solid #a9b5c1; }
570+
571+
.ord-bg-longhand-wins {
572+
background: #ddeeff;
573+
background-color: #ffe6e6;
574+
}
575+
.ord-bg-shorthand-wins {
576+
background-color: #ffe6e6;
577+
background: #ddeeff;
578+
}
579+
580+
.ord-border-width-longhand-wins {
581+
border: 0.6pt solid #446;
582+
border-left-width: 3pt;
583+
}
584+
.ord-border-width-shorthand-wins {
585+
border-left-width: 3pt;
586+
border: 0.6pt solid #446;
587+
}
588+
589+
.ord-border-color-longhand-wins {
590+
border: 0.8pt solid #999;
591+
border-right-color: #cc0000;
592+
}
593+
.ord-border-color-shorthand-wins {
594+
border-right-color: #cc0000;
595+
border: 0.8pt solid #999;
596+
}
597+
</style>
598+
599+
<div class="ord-wrap">
600+
<div class="ord-case ord-bg-longhand-wins">
601+
<strong>Case A: background then background-color</strong>
602+
<code>background: #ddeeff; background-color: #ffe6e6;</code>
603+
Expected fill: pink (#ffe6e6).
604+
</div>
605+
606+
<div class="ord-case ord-bg-shorthand-wins">
607+
<strong>Case B: background-color then background</strong>
608+
<code>background-color: #ffe6e6; background: #ddeeff;</code>
609+
Expected fill: light blue (#ddeeff).
610+
</div>
611+
612+
<div class="ord-case ord-border-width-longhand-wins">
613+
<strong>Case C: border then border-left-width</strong>
614+
<code>border: 0.6pt solid #446; border-left-width: 3pt;</code>
615+
Left border should be much thicker than other sides.
616+
</div>
617+
618+
<div class="ord-case ord-border-width-shorthand-wins">
619+
<strong>Case D: border-left-width then border</strong>
620+
<code>border-left-width: 3pt; border: 0.6pt solid #446;</code>
621+
Left border should revert to same thickness as other sides.
622+
</div>
623+
624+
<div class="ord-case ord-border-color-longhand-wins">
625+
<strong>Case E: border then border-right-color</strong>
626+
<code>border: 0.8pt solid #999; border-right-color: #cc0000;</code>
627+
Right border should be red; other sides gray.
628+
</div>
629+
630+
<div class="ord-case ord-border-color-shorthand-wins">
631+
<strong>Case F: border-right-color then border</strong>
632+
<code>border-right-color: #cc0000; border: 0.8pt solid #999;</code>
633+
All sides should be gray (right red override should be overwritten).
634+
</div>
635+
</div>
359636
HTML;
360637

361638
$pdf->addHTMLCell($html, 15, 18, 180, 0);

src/CSS.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,14 @@ protected function implodeCSSData(array $css): string
588588
continue;
589589
}
590590

591+
// Keep declaration order aligned with the latest winning occurrence.
592+
// This preserves shorthand/longhand cascade behavior across merged rules.
593+
$orderIdx = \array_search($key, $order, true);
594+
if ($orderIdx !== false) {
595+
unset($order[$orderIdx]);
596+
}
597+
$order[] = $key;
598+
591599
$decls[$key] = [
592600
'name' => $name,
593601
'value' => $value,

0 commit comments

Comments
 (0)