generated from jric11/baseProject
Initial commit
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* E080_no_write_regions.php
|
||||
*
|
||||
* @since 2026-06-16
|
||||
* @category Library
|
||||
* @package Pdf
|
||||
* @author Nicola Asuni <info@tecnick.com>
|
||||
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
|
||||
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
|
||||
* @link https://github.com/tecnickcom/tc-lib-pdf
|
||||
*
|
||||
* This file is part of tc-lib-pdf software library.
|
||||
*
|
||||
* No-write page regions (legacy TCPDF example_064 equivalent).
|
||||
*
|
||||
* A no-write region is a portion of the page that flowing text will avoid. Each region is
|
||||
* defined exactly as in the legacy library: a vertical, possibly slanted, segment plus the
|
||||
* page side it blocks ('L' or 'R'). tc-lib-pdf-page approximates the resulting writable area
|
||||
* with a stack of rectangular regions (one per horizontal band), so text flows down the page
|
||||
* hugging the obstacle. Several adjacent segments approximate curved shapes (see the circle on
|
||||
* the second scenario). See Com\Tecnick\Pdf\Page\Region::setNoWriteRegions().
|
||||
*
|
||||
* Each obstacle scenario is rendered twice, on consecutive pages, so the two text engines can be
|
||||
* compared side by side:
|
||||
* - with addTextCell() (plain flowing text);
|
||||
* - with addHTMLCell() (HTML cell), producing the SAME visual result.
|
||||
* Both engines hug the rectangular, trapezoidal and circular obstacles band by band and, when
|
||||
* the text overflows the last region, continue on a fresh full-width page (no-write regions are
|
||||
* page-specific, so the continuation page has no obstacles).
|
||||
*
|
||||
* NOTE: the band height must be at least one text line tall; with a 12pt font (~4.9mm line
|
||||
* height) a 5mm band fits exactly one line, giving the finest (per-line) staircase.
|
||||
*/
|
||||
|
||||
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
|
||||
// trusted paths that covers this package tree, so run the examples in place. To read assets
|
||||
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
|
||||
// parameter (see E047_remote_resources_security.php).
|
||||
|
||||
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
|
||||
|
||||
// autoloader when using Composer
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// define fonts directory
|
||||
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
|
||||
|
||||
// autoloader when using RPM or DEB package installation
|
||||
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
|
||||
|
||||
// main TCPDF object
|
||||
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
|
||||
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
|
||||
isunicode: true,
|
||||
subsetfont: false,
|
||||
compress: true,
|
||||
mode: \Com\Tecnick\Pdf\PdfConformance::None,
|
||||
objEncrypt: null,
|
||||
);
|
||||
|
||||
// ----------
|
||||
|
||||
$pdf->setCreator('tc-lib-pdf');
|
||||
$pdf->setAuthor('Nicola Asuni');
|
||||
$pdf->setSubject('tc-lib-pdf example: 080');
|
||||
$pdf->setTitle('No-write page regions');
|
||||
$pdf->setKeywords('TCPDF tc-lib-pdf example no-write page regions wrap text shape');
|
||||
$pdf->setPDFFilename('080_no_write_regions.pdf');
|
||||
|
||||
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
|
||||
|
||||
// ----------
|
||||
// Fonts (matching legacy example_064: 12pt body, small 8pt annotation labels).
|
||||
// NOTE: the font engine keeps the LAST inserted font as the current one, so each font is
|
||||
// (re)inserted with insertFont() right before it is used, making it the active font.
|
||||
|
||||
$insertFont = static function (string $style, float $size) use ($pdf): void {
|
||||
$font = $pdf->font->insert($pdf->pon, 'helvetica', $style, $size);
|
||||
$pdf->page->addContent($font['out']);
|
||||
};
|
||||
|
||||
// ----------
|
||||
// Colours (matching legacy example_064).
|
||||
|
||||
$black = $pdf->color->getPdfColor('rgb(0,0,0)'); // titles and labels (single-line, no page break)
|
||||
$bodyBlue = 'rgb(0,0,255)'; // flowing body text (justified, as in example_064)
|
||||
|
||||
// Graphic styles: light-green fill, the no-write edge in red, the other edges in grey dashed.
|
||||
$fillStyle = [
|
||||
'lineWidth' => 0.254,
|
||||
'lineCap' => 'butt',
|
||||
'lineJoin' => 'miter',
|
||||
'dashArray' => [1, 1],
|
||||
'dashPhase' => 0,
|
||||
'lineColor' => 'rgb(127,127,127)',
|
||||
'fillColor' => 'rgb(220,255,220)',
|
||||
];
|
||||
$edgeGrey = [
|
||||
'lineWidth' => 0.254,
|
||||
'lineCap' => 'butt',
|
||||
'lineJoin' => 'miter',
|
||||
'dashArray' => [1, 1],
|
||||
'dashPhase' => 0,
|
||||
'lineColor' => 'rgb(127,127,127)',
|
||||
];
|
||||
$edgeRed = [
|
||||
'lineWidth' => 0.254,
|
||||
'lineCap' => 'butt',
|
||||
'lineJoin' => 'miter',
|
||||
'dashArray' => [],
|
||||
'dashPhase' => 0,
|
||||
'lineColor' => 'rgb(255,0,0)',
|
||||
];
|
||||
$arrowStyle = [
|
||||
'lineWidth' => 0.254,
|
||||
'lineCap' => 'butt',
|
||||
'lineJoin' => 'miter',
|
||||
'dashArray' => [],
|
||||
'dashPhase' => 0,
|
||||
'lineColor' => 'rgb(0,0,0)',
|
||||
];
|
||||
|
||||
// Image used as a rectangular obstacle (legacy example used images/image_demo.jpg).
|
||||
$imgFile = \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-image/test/images/200x100_RGBICC.jpg');
|
||||
$imgId = $pdf->image->add($imgFile);
|
||||
|
||||
// Flowing body text (kept blue and justified as in example_064).
|
||||
$intro =
|
||||
'TEST PAGE REGIONS: a no-write region is a portion of the page with a rectangular or '
|
||||
. 'trapezium shape that will not be covered when writing text. A region is aligned on the '
|
||||
. 'left or right side of the page and is defined using a vertical segment. You can set '
|
||||
. 'multiple regions for the same page, and combine several adjacent regions to approximate '
|
||||
. 'curved shapes.';
|
||||
|
||||
$lorem =
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus '
|
||||
. 'quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet '
|
||||
. 'vitae augue. Sed vel velit erat. Class aptent taciti sociosqu ad litora torquent per conubia '
|
||||
. 'nostra, per inceptos himenaeos. Cras eget velit nulla, eu sagittis elit. Nunc ac arcu est, in '
|
||||
. 'lobortis tellus. Praesent condimentum rhoncus sodales. In hac habitasse platea dictumst. Proin '
|
||||
. 'porta eros pharetra enim tincidunt dignissim nec vel dolor. Cras sapien elit, ornare ac '
|
||||
. 'dignissim eu, ultricies ac eros. Maecenas augue magna, ultrices a congue in, mollis eu nulla. '
|
||||
. 'Nunc venenatis massa at est eleifend faucibus. Vivamus sed risus lectus, nec interdum nunc. '
|
||||
. 'Fusce et felis vitae diam lobortis sollicitudin. Aenean tincidunt accumsan nisi, id vehicula '
|
||||
. 'quam laoreet elementum. Phasellus egestas interdum erat, et viverra ipsum ultricies ac.';
|
||||
|
||||
// More text than fits on a no-write page: with automatic page break enabled the flow continues
|
||||
// on a fresh page. No-write regions are page-specific, so that continuation page has no
|
||||
// obstacles and the text returns to the full page width.
|
||||
$body = $intro . ' ' . \str_repeat($lorem . ' ', 5);
|
||||
|
||||
$margin = [
|
||||
'PL' => 15.0,
|
||||
'PR' => 15.0,
|
||||
'CT' => 25.0,
|
||||
'CB' => 12.0,
|
||||
];
|
||||
$bandHeight = 5.0;
|
||||
|
||||
// =============================================================
|
||||
// Reusable building blocks, so each obstacle scenario can be rendered identically with the
|
||||
// plain-text engine (addTextCell) and the HTML engine (addHTMLCell).
|
||||
|
||||
// Add a page and print its (black) title.
|
||||
$startPage = static function (string $title) use ($pdf, $margin, $black, $insertFont): array {
|
||||
$page = $pdf->addPage(['margin' => $margin]);
|
||||
$pdf->page->addContent($black);
|
||||
$insertFont('B', 13);
|
||||
$pdf->addTextCellXY(txt: $title, posx: 15, posy: 12, drawcell: false);
|
||||
|
||||
return $page;
|
||||
};
|
||||
|
||||
// Draw the two image obstacles plus the two trapezoids (with xt,yt / xb,yb / side labels and
|
||||
// direction arrows, as in the legacy example) and register the matching no-write regions.
|
||||
$setupTrapezoids = static function (float $pageHeight) use (
|
||||
$pdf,
|
||||
$imgId,
|
||||
$fillStyle,
|
||||
$edgeGrey,
|
||||
$edgeRed,
|
||||
$arrowStyle,
|
||||
$black,
|
||||
$insertFont,
|
||||
$bandHeight,
|
||||
): void {
|
||||
// Two image obstacles: top-right and bottom-left.
|
||||
$pdf->page->addContent($pdf->image->getSetImage($imgId, 155, 30, 40, 40, $pageHeight));
|
||||
$pdf->page->addContent($pdf->image->getSetImage($imgId, 15, 230, 40, 40, $pageHeight));
|
||||
|
||||
// Left trapezoid: the slanted right edge (57,90)-(67,140) is the no-write boundary (red).
|
||||
$pdf->page->addContent($pdf->graph->getPolygon([15, 90, 57, 90, 67, 140, 15, 140, 15, 90], 'DF', [
|
||||
'all' => $fillStyle,
|
||||
0 => $edgeGrey,
|
||||
1 => $edgeRed,
|
||||
2 => $edgeGrey,
|
||||
3 => $edgeGrey,
|
||||
]));
|
||||
|
||||
// Right trapezoid: the slanted left edge (155,180)-(145,130) is the no-write boundary (red).
|
||||
$pdf->page->addContent($pdf->graph->getPolygon([145, 130, 195, 130, 195, 180, 155, 180, 145, 130], 'DF', [
|
||||
'all' => $fillStyle,
|
||||
0 => $edgeGrey,
|
||||
1 => $edgeGrey,
|
||||
2 => $edgeGrey,
|
||||
3 => $edgeRed,
|
||||
]));
|
||||
|
||||
// Annotation labels and direction arrows (8pt black), as in the legacy example.
|
||||
$pdf->page->addContent($black);
|
||||
$insertFont('', 8);
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'xt,yt',
|
||||
posx: 38,
|
||||
posy: 90,
|
||||
width: 18,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'xb,yb',
|
||||
posx: 48,
|
||||
posy: 136,
|
||||
width: 18,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'side',
|
||||
posx: 38,
|
||||
posy: 111,
|
||||
width: 16,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->page->addContent($pdf->graph->getArrow(60, 115, 40, 115, 2, 4, 15, $arrowStyle));
|
||||
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'xt,yt',
|
||||
posx: 147,
|
||||
posy: 130,
|
||||
width: 18,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'xb,yb',
|
||||
posx: 156,
|
||||
posy: 176,
|
||||
width: 18,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->addTextCellXY(
|
||||
txt: 'side',
|
||||
posx: 158,
|
||||
posy: 151,
|
||||
width: 16,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
||||
drawcell: false,
|
||||
);
|
||||
$pdf->page->addContent($pdf->graph->getArrow(152, 155, 172, 155, 2, 4, 15, $arrowStyle));
|
||||
|
||||
// No-write regions: each is a segment (xt,yt)-(xb,yb) plus the blocked page side.
|
||||
$pdf->page->setNoWriteRegions(
|
||||
[
|
||||
['xt' => 153, 'yt' => 30, 'xb' => 153, 'yb' => 70, 'side' => 'R'], // top-right image
|
||||
['xt' => 60, 'yt' => 90, 'xb' => 70, 'yb' => 140, 'side' => 'L'], // left trapezoid
|
||||
['xt' => 143, 'yt' => 130, 'xb' => 153, 'yb' => 180, 'side' => 'R'], // right trapezoid
|
||||
['xt' => 58, 'yt' => 230, 'xb' => 58, 'yb' => 270, 'side' => 'L'], // bottom-left image
|
||||
],
|
||||
$bandHeight,
|
||||
);
|
||||
};
|
||||
|
||||
// Draw the circular obstacle (left half-disc against the right content edge) traced by eight
|
||||
// adjacent right-side segments, and register the matching no-write regions.
|
||||
$setupCircle = static function () use ($pdf, $fillStyle, $bandHeight): void {
|
||||
$pdf->page->addContent($pdf->graph->getPolygon(
|
||||
[
|
||||
195,
|
||||
110,
|
||||
179.693,
|
||||
113.045,
|
||||
166.716,
|
||||
121.716,
|
||||
158.045,
|
||||
134.693,
|
||||
155,
|
||||
150,
|
||||
158.045,
|
||||
165.307,
|
||||
166.716,
|
||||
178.284,
|
||||
179.693,
|
||||
186.955,
|
||||
195,
|
||||
190,
|
||||
],
|
||||
'DF',
|
||||
['all' => $fillStyle],
|
||||
));
|
||||
|
||||
$pdf->page->setNoWriteRegions([
|
||||
['xt' => 195, 'yt' => 110, 'xb' => 179.693, 'yb' => 113.045, 'side' => 'R'],
|
||||
['xt' => 179.693, 'yt' => 113.045, 'xb' => 166.716, 'yb' => 121.716, 'side' => 'R'],
|
||||
['xt' => 166.716, 'yt' => 121.716, 'xb' => 158.045, 'yb' => 134.693, 'side' => 'R'],
|
||||
['xt' => 158.045, 'yt' => 134.693, 'xb' => 155, 'yb' => 150, 'side' => 'R'],
|
||||
['xt' => 155, 'yt' => 150, 'xb' => 158.045, 'yb' => 165.307, 'side' => 'R'],
|
||||
['xt' => 158.045, 'yt' => 165.307, 'xb' => 166.716, 'yb' => 178.284, 'side' => 'R'],
|
||||
['xt' => 166.716, 'yt' => 178.284, 'xb' => 179.693, 'yb' => 186.955, 'side' => 'R'],
|
||||
['xt' => 179.693, 'yt' => 186.955, 'xb' => 195, 'yb' => 190, 'side' => 'R'],
|
||||
], $bandHeight);
|
||||
};
|
||||
|
||||
// Flow the body with the plain-text engine. width 0 makes each band use its own writable width.
|
||||
// The fill colour is set through the graph so it is tracked and re-applied automatically when the
|
||||
// flow continues on an automatically added page (the colour resets per page).
|
||||
$renderTextBody = static function () use ($pdf, $body, $bodyBlue, $insertFont): void {
|
||||
$pdf->page->addContent($pdf->graph->add(['fillColor' => $bodyBlue]));
|
||||
$insertFont('', 12);
|
||||
$pdf->addTextCell(
|
||||
txt: $body,
|
||||
posx: 0,
|
||||
posy: 0,
|
||||
width: 0,
|
||||
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
|
||||
drawcell: false,
|
||||
);
|
||||
};
|
||||
|
||||
// Flow the SAME body with the HTML engine. A single justified, coloured paragraph hugs the
|
||||
// obstacle band by band exactly like addTextCell, and overflows onto a fresh full-width page.
|
||||
// A font must be active before addHTMLCell(). getHTMLCell() returns the same rendering as a
|
||||
// string instead of emitting it (e.g. $code = $pdf->getHTMLCell(...); $pdf->page->addContent($code);).
|
||||
$renderHtmlBody = static function () use ($pdf, $body, $bodyBlue, $insertFont): void {
|
||||
$insertFont('', 12);
|
||||
$region = $pdf->page->getRegion();
|
||||
$html = '<p style="text-align:justify;color:' . $bodyBlue . ';">' . $body . '</p>';
|
||||
$pdf->addHTMLCell(
|
||||
html: $html,
|
||||
posx: (float) $region['RX'],
|
||||
posy: (float) $region['RY'],
|
||||
width: (float) $region['RW'],
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// SCENARIO 1: rectangular and trapezoidal no-write regions around two images.
|
||||
|
||||
$page = $startPage('No-write page regions: addTextCell flowing around rectangular and trapezoidal shapes');
|
||||
$pageHeight = (float) $page['height'];
|
||||
$setupTrapezoids($pageHeight);
|
||||
$renderTextBody();
|
||||
|
||||
$startPage('No-write page regions: addHTMLCell flowing around rectangular and trapezoidal shapes');
|
||||
$setupTrapezoids($pageHeight);
|
||||
$renderHtmlBody();
|
||||
|
||||
// =============================================================
|
||||
// SCENARIO 2: eight adjacent regions approximating a circle (text wraps its left side).
|
||||
|
||||
$startPage('No-write page regions: addTextCell flowing around a circular shape (8 adjacent segments)');
|
||||
$setupCircle();
|
||||
$renderTextBody();
|
||||
|
||||
$startPage('No-write page regions: addHTMLCell flowing around a circular shape (8 adjacent segments)');
|
||||
$setupCircle();
|
||||
$renderHtmlBody();
|
||||
|
||||
// =============================================================
|
||||
|
||||
$rawpdf = $pdf->getOutPDFString();
|
||||
|
||||
$pdf->renderPDF(rawpdf: $rawpdf);
|
||||
Reference in New Issue
Block a user