SetaPDF Demos

Fit Pages

This demo shows how to adjust the format of all pages in a document.

Thereby the original page is going to get scaled and centered, so that it fits into the new format.

For demonstration purpose the original document is created from scratch to show or test the behavior.

PHP
<?php

// load and register the autoload function
require_once __DIR__ . '/../../../../../bootstrap.php';

// create a string writer for a temporary result
$writer = new SetaPDF_Core_Writer_String();
// create a document
$document = new SetaPDF_Core_Document($writer);

// the wanted format
$wantedFormat = [200, 100];

// get the pages helper
$pages = $document->getCatalog()->getPages();

// create a new font
$font = SetaPDF_Core_Font_Standard_Courier::create($document);

// create an array with different formats
$formats = [
    [ // 1
        SetaPDF_Core_PageFormats::A4,
        "A4\n" . SetaPDF_Core_PageFormats::A4,
        0,
        70,
    ],
    [ // 2
        [100, 100, 0, 0],
        "100x100\n100,100,0,0",
        90,
        10,
    ],
    [ // 3
        [0, 0, 200, 200],
        "200x200\n0,0,200,200",
        90,
        10,
    ],
    [ // 4
        [-200, -700, 200, 700],
        "400x1400\n-200,-700,200,700",
        90,
        30,
    ],
    [ // 5
        [10, 50, 210, 550],
        "200x500\n10,50,210,550",
        90,
        20,
    ],
    [ // 6
        [-210, -550, -10, -50],
        "200x500\n-210,-550,-10,-50",
        -90,
        15,
    ],
    [ // 7
        [-10, -50, -210, -550],
        "200x500\n-10,-50,-210,-550",
        270,
        15,
    ],
    [ // 8
        [-100, 100, -200, -100],
        "100x200\n-100,100,-200,-100",
        360,
        7,
    ],
    [ // 9
        [2000, 5000, 4000, 2000],
        "2000x3000\n2000,5000,4000,2000",
        180,
        150,
    ],
    [ // 10
        [50, 10, 550, 210],
        "500x200\n50,10,550,210",
        0,
        50,
    ],
    [ // 11
        [2000, -3900, 2500, -3000],
        "500x900\n2000,-3900,2500,-3000",
        0,
        30,
    ]
];

// create the pages
foreach ($formats as $formatArray) {
    list($format, $formatName, $rotation, $fontSize) = $formatArray;

    // create a new page with the given format
    $page = $pages->create($format);

    // set the rotation
    $page->setRotation($rotation);

    // get the canvas
    $canvas = $page->getCanvas();

    // set the line width
    $canvas->path()->setLineWidth(10);

    // draw a rectangle around the page
    $canvas->draw()->rect(
        $page->getCropBox()->llx + 5,
        $page->getCropBox()->lly + 5,
        $page->getCropBox()->urx - $page->getCropBox()->llx - 10,
        $page->getCropBox()->ury - $page->getCropBox()->lly - 10
    );

    // create a new text block
    $text = new SetaPDF_Core_Text_Block($font, $fontSize);

    // set the align
    $text->setAlign(SetaPDF_Core_Text::ALIGN_CENTER);

    // set the text of the text block
    $text->setText($formatName);

    // draw the text in the center
    $x = ($page->getCropBox()->getUrx() - $page->getCropBox()->getWidth() / 2)  - $text->getWidth() / 2;
    $y = ($page->getCropBox()->getUry() - $page->getCropBox()->getHeight() / 2) - $text->getHeight() / 2;
    $text->draw($canvas, $x, $y);

    // create an annotation for demonstration purpose
    $annotation = new SetaPDF_Core_Document_Page_Annotation_Square(
        [$x, $y, $x + $text->getWidth(), $y + $text->getHeight()]
    );

    // set the color of the annotation
    $annotation->setColor([0, 1, .2]);

    // add the annotation to the page
    $page->getAnnotations()->add($annotation);
}

// save and finish the temporary document
$document->save()->finish();

// transfer the document into a new instance
$fittedDocument = SetaPDF_Core_Document::load(
    new SetaPDF_Core_Reader_String($writer),
    new SetaPDF_Core_Writer_Http('fit.pdf', true)
);

// get the pages object
$pages = $fittedDocument->getCatalog()->getPages();

// get the page count
$pageCount = $pages->count();

// iterate through all pages
for ($pageNumber = 1; $pageNumber <= $pageCount; $pageNumber++) {
    // get the page object
    $page = $pages->getPage($pageNumber);

    // sanitize the current page format
    $format = SetaPDF_Core_PageFormats::getFormat($page->getWidthAndHeight(), SetaPDF_Core_PageFormats::ORIENTATION_AUTO);

    // check if the format of the current page is the desired format
    if (SetaPDF_Core_PageFormats::is($wantedFormat, $format)) {
        continue;
    }

    // determine the orientation of the page
    $orientation = $page->getOrientation();

    // align the wanted format to the page orientation
    $wantedFormatArray = SetaPDF_Core_PageFormats::getFormat($wantedFormat, $orientation);

    // check if the page is rotated
    $rotated = ($page->getRotation() / 90) % 2;

    // calculate the scale factors
    $scaleX = $wantedFormatArray['width'] / $format['width'];
    $scaleY = $wantedFormatArray['height'] / $format['height'];

    // switch scale values if the page is rotated
    if ($rotated) {
        list($scaleX, $scaleY) = [$scaleY, $scaleX];
    }

    // take the smaller value as the scaling factor
    $scale = min($scaleX, $scaleY);

    // let's scale the page content
    $canvas = $page->getCanvas();
    $contents = $page->getContents();

    // we add a separate content stream before the existing stream
    $stream = $contents->prependStream(true);
    // save the graphic state
    $canvas->saveGraphicState();

    // get the old boundary
    $cropBox = $page->getBoundary();

    // calculate the scaling
    $rect = new SetaPDF_Core_Geometry_Rectangle(
        $cropBox->getLlx() * $scale,
        $cropBox->getLly() * $scale,
        $cropBox->getUrx() * $scale,
        $cropBox->getUry() * $scale
    );

    // calculate the values for centering
    $centerX = ($wantedFormatArray['width'] - (!$rotated ? $rect->getWidth() : $rect->getHeight())) / 2;
    $centerY = ($wantedFormatArray['height'] - (!$rotated ? $rect->getHeight() : $rect->getWidth())) / 2;

    // switch scale values if page is rotated
    if ($rotated) {
        list($centerX, $centerY) = [$centerY, $centerX];
    }

    // calculate the shift that is caused by the scale and add the centering value
    $translateX = $cropBox->getLlx() - $rect->getLl()->getX() + $centerX;
    $translateY = $cropBox->getLly() - $rect->getLl()->getY() + $centerY;

    // let's translate the canvas
    $canvas->translate($translateX, $translateY);

    // let's scale
    $canvas->scale($scale, $scale);

    // append a closing content stream
    $stream = $contents->pushStream(true);
    // and restore the opened graphic state
    $canvas->restoreGraphicState();

    // let's adjust the boundary boxes
    $boxes = SetaPDF_Core_PageBoundaries::$all;
    // reverse the boxes order to pass valid boxes to the page
    if ($scale < 1) {
        $boxes = array_reverse($boxes);
    }

    // iterate through all possible boundaries
    foreach ($boxes AS $boxName) {
        $_box = $page->getBoundary($boxName, false, true);
        if ($_box === false) {
            continue;
        }

        // create a new boundary box
        $box = SetaPDF_Core_DataStructure_Rectangle::byArray(
            [
                $_box->getLlx(),
                $_box->getLly(),
                $_box->getUrx() * $scaleX + ($_box->getLlx() - ($_box->getLlx() * $scaleX )),
                $_box->getUry() * $scaleY + ($_box->getLly() - ($_box->getLly() * $scaleY ))
            ]
        );

        // reset the boundary box
        $page->setBoundary($box, $boxName, false);
    }

    // now we adjust the coords of all annotation objects
    $annotations = $page->getAnnotations();
    $all = $annotations->getAll();
    foreach ($all AS $annotation) {
        $dict = $annotation->getDictionary();

        // the size of a popup annotation has to be handled individually
        // we just use the scale and translate values which were calculated before
        if ($annotation instanceof SetaPDF_Core_Document_Page_Annotation_Popup) {
            $_rect = $annotation->getRect();

            $rect = SetaPDF_Core_DataStructure_Rectangle::byArray([
                $_rect->getLlx() * $scale + $translateX,
                $_rect->getLly() * $scale + $translateY + $_rect->getHeight(),
                $_rect->getLlx() * $scale + $translateX + $_rect->getWidth(),
                $_rect->getUry() * $scale + $translateY
            ]);
            $dict['Rect'] = $rect->getValue();
            continue;
        }

        $_rect = $annotation->getRect();
        $rect = SetaPDF_Core_DataStructure_Rectangle::byArray([
            $_rect->getLlx() * $scale + $translateX,
            $_rect->getLly() * $scale + $translateY,
            $_rect->getUrx() * $scale + $translateX,
            $_rect->getUry() * $scale + $translateY
        ]);

        $dict['Rect'] = $rect->getValue();
    }
}

// save the document
$fittedDocument->save()->finish();