On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • Okay, after much asking for it I guess I’ll share my snippet

    It works like this: http://nimja.com/frozen-youth

    That is a dynamic PDF file with the background and copyright automatically added for each page. My whole story is in there automatically and is always up to date.

    Current version: 0.8

    Version history:
    v0.8 - Added Title and Author support to the snippet call
    v0.7 - Fixed the ’deprecated’ error that prevented PDFs from being proper.
    v0.6 - Added multiple parents support.
    v0.5 - Italic font support, limited to whole lines/paragraphs.
    v0.4 - Optimizations for 100+ pages.
    v0.3 - Added header and footer.
    v0.2 - Numerous fixes
    v0.1 - Basic work.

    Libraries used:
    modified FPDI - Added setTemplate function. - included in the download of this post.
    original available at: http://www.setasign.de/products/pdf-php-solutions/fpdi/

    Install:
    Download the files at the bottom of this post and upload them to your server so the files reside in YOUR_SITE/assets/snippets/makepdf/
    Create the snippet with the name MakePDF from the code below. There is an error when trying to upload the zip file to this thread: The attachments upload directory is not writable. Your attachment or avatar cannot be saved.
    Add application/pdf to the Custom content types: (in Tools -> Configuration)

    Sorry, while the file upload doesn’t work, download the file here: makepdf.zip

    Usage:
    Create an new document:
    set "Uses template" to (blank)
    set "Document’s alias" to something.pdf
    set "Content Type" to application/pdf
    Call the snippet:
    [!MakePDF? &parent=`1` !] (see the snippet for more options)

    Notes:
    I’ve used several PHP->PDF libraries but only FPDF (and FPDI) has good enough performance and can handle documents over 50 pages. Do note that using a template uses more memory and I had to abandon templates over about 300 pages.

    Please note that this is very much a work in progress and supposed to be educational. It works, but that’s about it. Please feel free to point out any/all obvious mistakes I’ve made.

    Snippet Code:
    For the curious or the lazy.
    <?php
    /* MakePDF snippet:
    
    v0.8 - Added Title and Author support to the snippet call and fixed the filename not working.
    v0.7 - Fixed the 'deprecated' error that prevented PDFs from being proper.
    v0.6 - Added multiple parents support.
    v0.5 - Italic font support, limited to whole lines/paragraphs.
    v0.4 - Optimizations for 100+ pages.
    v0.3 - Added header and footer.
    v0.2 - Numerous fixes
    v0.1 - Basic work.
    
    By Nimja - nimja.com
    
    Usage:
     [!MakePDF? &parent=`1` !] -- Basic example
     [!MakePDF? &parent=`1,2,3` !]  -- Multiple parents
     [!MakePDF? &parent=`1,2,3` &sortBy=`pub_date` $sortDir=`asc` &where=`template=1` &filename=`Frozen_Youth.pdf` &title=`Frozen Youth` $author=`Nimja.com` &limit=`10`!] -- Additional settings.
     
    */
    
    
    /* ------------------------------------------
    			Basic settings
    ------------------------------------------- */
    if (isset($parent)) {
    	//Title, above each page.
    	$pdfTitle = (!empty($title)) ?  $title : 'Frozen Youth';
    
    	//Author, below each page with copyright.
    	$pdfAuthor = (!empty($author)) ?  $author : 'Nimja';
    	
    	// &sortBY, field to sort by, defaults to pub_date.
    	$sortBy = (!empty($sortBy) ) ? $sortBy : 'menuindex';
    	
    	// &sortHow, sort direction, defaults to ASC(ending).
    	//	Options are: ASC, DESC
    	$sortHow = (!empty($sortHow) ) ? $sortHow : 'ASC';
    	
    	//Additional 'filters'
    	$where = (!empty($where) ) ? $where : '';
    
    	//&filename - The filename of the PDF people will download.
    	$filename = (!empty($filename) ) ? $filename : 'Download.pdf';
    
    
    	//&limit - Max number of pages we will retrieve. Leave empty or 0 for all.
    	$limit = (!empty($limit) ) ? $limit : '';
    	
    	//Fields we use.
    	$fields = 'longtitle,description,content';
    	
    	$basepath = $modx->config['base_path']."assets/snippets/makepdf/";
    	$classpath = $basepath.'class/';
    
    
    	//Use MODX function to get children.
    	$parents = split(',', $parent);
    	$pages = Array();
    	foreach($parents as $par) {
    		$pages = array_merge($pages,$modx->getDocumentChildren($par, 1, 0, $fields, $where, $sortBy, $sortHow, $limit));
    	}
    
    	require_once($classpath.'fpdi.php');
    
    /* ------------------------------------------
    			Header and Footer to class
    ------------------------------------------- */
    
    
    
    	class PDF extends FPDI {
    		function Header() {
    		    $this->SetFont('Arial','I',11);
    		    //Position at 1.5 cm from bottom
    		    $this->SetY(5);
    		    //Page number
    		    $this->Cell(0,10,$this->title,0,1,'R');
    		}
    		function Footer() {
    		    $this->SetFont('Arial','I',8);
    		    //Position at 1.5 cm from bottom
    		    $this->SetY(-15);
    		    //Copyright based on year and author.
    		    $this->Cell(0,10,'Copyright '.date("Y").' '.$this->author,0,0,'L');
    		    //Position at 1.5 cm from bottom
    		    $this->SetY(-15);
    		    //Page number
    		    $this->Cell(0,10,$this->PageNo(),0,0,'R');
    		}
    	}	
    
    /* ------------------------------------------
    			Start PDF and set the settings.
    ------------------------------------------- */
    
    	// initiate FPDI 
    	$pdf = new PDF(); 
    	// add a page 
    	$pdf->AddPage(); 
    	// set the sourcefile 
    	$pdf->setSourceFile($basepath.'Page.pdf'); 
    	$pdf->SetAuthor($pdfAuthor);
    	$pdf->SetCreator($pdfAuthor);
    	$pdf->SetTitle($pdfTitle);
    	
    	// import page 1 
    	$templatePage = $pdf->importPage(1); 
    	// use the imported page and place it on the first page. After this it will be added to every page.
    	$pdf->useTemplate($templatePage);
    	// set the template to be applied automatically.
    	$pdf->setTemplate($templatePage); 
    
    	$pdf->SetFont('Arial'); 
    	$pdf->SetTextColor(0,0,0); 
    	$pdf->SetLineWidth(.25);
    	$pdf->SetDrawColor(0,0,0);
    	$pdf->SetMargins(10,10);
    	
    	$newpage = false;
    
    	$remove = Array("\r", '<b>', '</b>');
    	$clean = Array('<i>', '</i>');
    
    
    /* ------------------------------------------
    			Start content.
    ------------------------------------------- */
    
    	$c = 0; //MODx Page counter, not PDF page!
    	$italic = false;
    	foreach ($pages as $page) {
    		//echo $page['longtitle'].' - '.$page['introtext'].'<br />';
    		$chapter = $page['longtitle'];
    		$title = $page['description'];
    		$content = str_replace($remove, '', $page['content']);
    		
    		//Start at the top of a page after 10 chapters. Because I'm base-10 addicted.
    		if ($c > 9) {
    			$pdf->AddPage();
    			$c=0;
    		}
    
    		//Set the title.
    		$pdf->SetX(10);
    		$pdf->SetFont('Arial','',18);
    		$pdf->Cell(0,7,$chapter.' - '.$title,'B',1,'L');
    
    		//Add some space between the title and the content.
    		$pdf->Ln(3);
    
    		$content = split("\n", $content);
    
    		//Handle content per line/block.		
    		foreach ($content as $par) {
    			$line = trim($par);
    
    			//Space between paragraphs (a little less than a whole line, looks nicer).
    			if (empty($line) ) {
    				$pdf->Ln(3);
    				
    			} else {
    				if (strpos($line, '<i>') > -1) $italic = true;
    
    				//Switch between italic and normal. Only usable for whole paragraphs/lines for now.
    				if ($italic) {
    					$pdf->SetFont('Arial','i',11);
    				} else {
    					$pdf->SetFont('Arial','',11);
    				}
    				if (strpos($line, '</i>')) $italic = false;
    				
    				$line = str_replace($clean, '', $line);
    				$pdf->MultiCell(0,4.5,$line,0,1,'L');
    			}
    		}
    		$pdf->Ln(10);
    		$italic = false;
    		$c++;
    	}
    
    	$pdf->Output($filename, 'd'); 
    	
    } else {
    	//Output error message because of missing parent ID
    	echo 'Parent ID missing!';
    }
    ?>
      [font=Times]Comics, stories, music, graphics, games and more! http://Nimja.com
    • Thanks a lot for sharing !

      About the note, I don’t see how automated template would be wrong, looks like it’s an improvement huh
        .: COO - Commerce Guys - Community Driven Innovation :.


        MODx est l&#39;outil id
      • IMPORTANT: The following changes are supposed to be done to fpdf_tpl.php you should have installed in the MODX_INSTALLATION/assets/snippets/makepdf/class directory. ALWAYS MAKE A BACKUP OF FILES YOU ARE CHANGING!

        It’s a matter of programming style. I’m not going to mess with other people’s code and publish it without their permission. But I guess publishing the changes is alright.

        Anyway, I’ll post the changes separately for people to learn from. Please follow the instructions carefully.

        Usage: same as UseTemplate, but it’s automatically applied each AddPage and with automated overflow. (say when a text is longer then a page)
        Example: $pdf->setTemplate($templatePage);

        Add this variable to the top of the class: (sets up our ’current template’ for the automatic template.
            /**
             * Current SET Template-ID
             * @var int
             */
            var $curtpl = 0;


        Add this function after the useTemplate function:
            /**
             * setTemplate makes sure new pages always have the template.
             */
            function setTemplate($tplidx=null) {
            	//If the template ID is set and exists.
            	if (isset($this->tpls[$tplidx]) ) {
            		$this->curtpl = $tplidx;
            	} else {
            		$this->curtpl = 0;
            	}
        	}


        Replace the function AddPage in the class:
            /**
             * See FPDF-Documentation ;-)
             *
             * AddPage is not available when you're "in" a template.
             */
            function AddPage($orientation='') {
                if ($this->_intpl)
                    $this->Error('Adding pages in templates isn\'t possible!');
                parent::AddPage($orientation);
                //Apply the current template before any text is applied.
                if (!empty($this->curtpl)) {
                	$this->UseTemplate($this->curtpl);
                }
            }
          [font=Times]Comics, stories, music, graphics, games and more! http://Nimja.com
        • This looks great. Thanks for making this available.
            [font=Verdana]Shane Sponagle | [wiki] Snippet Call Anatomy | MODx Developer Blog | [nettuts] Working With a Content Management Framework: MODx

            Something is happening here, but you don&#39;t know what it is.
            Do you, Mr. Jones? - [bob dylan]
          • Great !

            Many thanks for this smiley
              .: COO - Commerce Guys - Community Driven Innovation :.


              MODx est l&#39;outil id
            • And there was me being affraid no one would even find this interesting...

              More’s the fool me.
                [font=Times]Comics, stories, music, graphics, games and more! http://Nimja.com
              • The original classes are released under the Apache License, version 2.

                FPDI was developed by Setasign - Jan Slabon - and is published under the Apache Software License, Version 2.0.
                From that license:
                2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
                Section 4 gives the conditions you must adhere to if you do distribute a Derivative Work.

                Therefor you are free to make a Derivative Work and distribute it. If the original author didn’t want end users to have that ability, he should choose a different license.

                http://www.apache.org/licenses/LICENSE-2.0.html
                  Studying MODX in the desert - http://sottwell.com
                  Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                  Join the Slack Community - http://modx.org
                • Super cool work Nimja. I just wish they had more of the capabilities found in their commercial PDF extensions available in free products. smiley
                    Ryan Thrash, MODX Co-Founder
                    Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
                  • I got this error

                    FPDF error: Unable to find pointer to xref table

                    I skipped something didnt I rolleyes

                    • I’ve never seen that error before. Make sure you download all 4!! libraries.
                      FPDI
                      FPDF_TPL
                      FPDF (from the original website)

                      Odd to get that error though.
                        [font=Times]Comics, stories, music, graphics, games and more! http://Nimja.com