Getting Item ID number (same as in URL)

Good Morning OMEKA Forum -

I am trying to pull together an array of item ids (the ids used in the URLs), and because the template code I am building off of is pulling elements from Dublin Core which is NOT where ItemID is - I am having trouble…see code example below, and thank you for answering my very basic question…

This line of code:
$creators = $item->getElementTexts('Dublin Core', 'Creator');
returns all the item creator records fitting the query, author essentially.

in a similar way, this returns the title of items matching the query, no problem.
$titles = $item->getElementTexts('Dublin Core', 'Title');

I just need to add the ItemID to this process. Unfortunately, ItemID is not in the Dublin Core, and replacing that with ‘Item Element Metadata’, ‘itemid’ doesnt work. ‘Item Element Metadata’ is a valid Item Element Type, as seen in MyPHPAdmin, however, I cannot get the code right to pull itemid from items that match search filtering…

Have been stuck for several hours now, hoping a more seasoned pro can get me back on track…

Thank you,
JC

Assuming you just mean Omeka’s internal ID, the one that shows up in the item show URLs, you can just say $item->id to get the ID.

(edit: and now I see you specified that in your thread title, so yeah)

1 Like

Hey @jflatnes - thank you for the insight. Here is the entire section I am grappling with (in Reports Plugin, part of PdfQrCode.php.

the first block, creates an array ($creators) based on getting the Dublin Core Metadata Element ‘Creator’. Creator and Title were easy to organize into the array because of the Dublin Core commonality.

in the second block, I am trying to print ItemID below Title and Creator. However, ItemID is not part of the Dublin Core Metadata Element - so its not as straight forward. I also do not think I am properly querying the ItemID metadata element into an array that can be unpacked alongside the Creator or Title arrays.

//Prints Creator under Title 

        $creators = $item->getElementTexts('Dublin Core', 'Creator');
        if (count($creators) > 0) {
            $textOriginX = $originX + self::LABEL_HEIGHT;
            $textOriginY = $originY + (0.6 * self::LABEL_HEIGHT) ;
            $cleanCreator = strip_tags(htmlspecialchars_decode($creators[0]->text));
            $this->_drawWrappedText(
                $page,
                $cleanCreator, 
                $textOriginX, 
                $textOriginY, 
                self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
            );   
        }

//Prints ID under Creator 
            $itemids = $item->id;           
            if (count($itemids) > 0) {
            $textOriginX = $originX + self::LABEL_HEIGHT;
            $textOriginY = $originY + (0.4 * self::LABEL_HEIGHT) ;
            $cleanItemids = strip_tags(htmlspecialchars_decode($itemids[0]->text));
            $this->drawWrappedText(
                    $page, 
                    $cleanItemids, 
                    $textOriginX, 
                    $textOriginY, 
                    self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
            );   
        }

So, I believe the problem is either:

  1. The first line of ItemID block is incorrectly calling itemIDs from search results metadata temp store, and either returning nothing or returning an incompatible array for merge with title or creator sets.

  2. I may be calling itemID at the wrong point, too far upstream. In that maybe I should just try callin the ItemID when it draws the text onto the label page.

.....
  $this->_drawWrappedText(
                $page,
                $item->id,
                $textOriginX, 
.....

Also, realizing this roadblock I am stuck at is moreso a result of my php naivety. maybe if I understood the syntax or language better, i could realize the simple mistake here - just am not that type of ninja yet, so I appreciate any insight - esp if it is something very simple…

THANK YOU!

Your issue is just that the code for dealing with text metadata is a little more complicated, and you don’t actually need any of that complexity for the IDs, as there’s only ever one ID per item.

Instead of this:

$itemids = $item->id;           
if (count($itemids) > 0) {
    $textOriginX = $originX + self::LABEL_HEIGHT;
    $textOriginY = $originY + (0.4 * self::LABEL_HEIGHT) ;
    $cleanItemids = strip_tags(htmlspecialchars_decode($itemids[0]->text));
    $this->drawWrappedText(
            $page, 
            $cleanItemids, 
            $textOriginX, 
            $textOriginY, 
            self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
    );   
}

You only need this:

$textOriginX = $originX + self::LABEL_HEIGHT;
$textOriginY = $originY + (0.4 * self::LABEL_HEIGHT) ;
$this->drawWrappedText(
        $page, 
        (string) $item->id, 
        $textOriginX, 
        $textOriginY, 
        self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
);

I was hoping for something that clear cut, but the PDF report is never generated - it just stays “In Progress” indefinitely.

I am wondering if I need to change $item->id to ‘text’ format ala:

$itemids = $item->id;
$cleanitemds = strip_tags(htmlspecialchars_decode($itemsids[0]->text

In this way, we know the data getting pushed thru to ‘drawWrappedText(’ is actually text encoded??

You don’t need any of that stuff. That’s only there to handle the possibility that text fields have HTML in them, and the $itemids[0] part is treating the ID as an array, which it isn’t.

You could paste the entire file or method with your latest changes… there’s a decent chance that the issue is just something simple, like a syntax error.

Thanks for sticking with me.

PdfQrCode.php in full below, also linked via gDrive here.

<?php
/**
 * @package Reports
 * @subpackage Generators
 * @copyright Copyright (c) 2011 Center for History and New Media
 * @license http://www.gnu.org/licenses/gpl-3.0.txt
 */
 
/**
 * Report generator for PDF output with QR Codes included.
 *
 * Note that the PDF coordinate system starts from the bottom-left corner
 * of the page, and all measurements are in points (1/72 of an inch).
 *
 * @package Reports
 * @subpackage Generators
 */
class Reports_Generator_PdfQrCode 
    extends Reports_Generator
    implements Reports_GeneratorInterface
{
    /**
     * The current font being used by the PDF document
     *
     * @var Zend_Pdf_Resource_Font
     */
    private $_font;
    
    /**
     * The base URL of the Omeka installation that spawned the report
     *
     * @var string
     */
    private $_baseUrl;
    
    private $_qrGenerator;

    /**
     * The page count when retrieving items from the database. 
     *
     * @var integer
     */
    private $_itemPageNum = 1;

    private $_pdfPageCount = 0;

    /**
     * Set of strings corresponding to the path of each intermediate report 
     * file. 
     *
     * @var array
     */
    private $_filePaths = array();

    private $_pagesPerFile = 30;

    // Spacing constants for 5163 labels, in points.
    
    const PAGE_HEIGHT = 792;
    const PAGE_WIDTH = 612;
    
    const MARGIN_LEFT = 13.5;
    const MARGIN_RIGHT = 36;
    const MARGIN_TOP = 36;
    const MARGIN_BOTTOM = 13.5;
    
    const COLUMNS = 2;
    const ROWS = 5;
    
    const HORIZONTAL_SPACING = 11.25;
    const VERTICAL_SPACING = 0;
    
    const LABEL_HEIGHT = 144;
    const LABEL_WIDTH = 288;
    
    const FONT_SIZE = 10;

    const QR_HEIGHT = 300;
    const QR_WIDTH = 300;

    public function setPagesPerFile($count)
    {
        $this->_pagesPerFile = $count;
    }
    
    /**
     * Creates and generates the PDF report for the items in the report.
     *
     * @param string $filePath The path to the zip that contains the generated
     * PDFs.
     */
    public function generateReport($filePath) 
    {
        if (!extension_loaded('zip')) {
            throw new RuntimeException("zip extension is required to bundle "
                . "report PDF files.");
        }
        $options = unserialize($this->_reportFile->options);
        $this->_baseUrl = $options['baseUrl'];
        $this->_font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);

        $fileSuffix = 1;
        $pdfPath = $this->_newPdf($filePath, $fileSuffix);
        while ($items = $this->_getItems()) {
            if ($this->_pdfPageCount >= $this->_pagesPerFile) {
                $fileSuffix++;
                $pdfPath = $this->_newPdf($filePath, $fileSuffix);
                _log("Created new PDF file '$pdfPath'");
                $this->_pdfPageCount = 0;
            }
            $this->_addItems($items, $pdfPath);
        }
        $this->_zipFiles($filePath);
        $this->_unlinkFiles();
        return $filePath;
    }

    private function _unlinkFiles()
    {
        foreach ($this->_filePaths as $path) {
            if (file_exists($path) && is_file($path)) {
                unlink($path);
                _log("Deleted temporary file '$path'");
            }
        }
    }

    private function _zipFiles($zipPath)
    {
        $zip = new ZipArchive();
        $zlibTrue = (extension_loaded('zlib'))? "true" : "false ";
        if ($zip->open($zipPath, ZIPARCHIVE::OVERWRITE ) !== true) {
            $error = $zip->getStatusString();
            throw new RuntimeException("ZipArchive cannot create '$zipPath': $error.");
        }
        foreach ($this->_filePaths as $path) {
            if (preg_match('/.*\-(part.+\.pdf)/', $path, $matches)) {
                $toFilename = $matches[1];
            } else {
                $toFilename = basename($path);
            }
            $zip->addFile($path, $toFilename);
            _log("Added '$toFilename' to zip '$zipPath'.");
        }
        _log("Closing zip file.");
        $zip->close();        
        _log(memory_get_peak_usage());
    }

    private function _newPdf($filePath, $fileSuffix)
    {
        $pdfPath = $this->_getTempFilename($filePath, $fileSuffix);
        $pdf = new Zend_Pdf();
        $pdf->save($pdfPath);
        $this->_filePaths[] = $pdfPath;
        return $pdfPath;
    }

    private function _getTempFilename($prefix, $count)
    {
        return $prefix . '-part' . $count . '.pdf';
    }

    private function _getItems()
    {
        $items = get_db()
            ->getTable('Item')
            ->findBy(
                $this->_params, 
                30, // TODO: Convert me to a constant.
                $this->_itemPageNum
            );
        $this->_itemPageNum++;
        return $items;
    }
    
    /**
     * Draw one label section for one item on the PDF document.
     *
     * @param int $column Horizontal index on the current page
     * @param int $row Vertical index on the current page
     * @param Item $item The item to report on
     */
    private function _drawItemLabel(
        Zend_Pdf_Page $page, 
        $column, 
        $row, 
        $item
    ) {
        // Start at the bottom left corner and count over for columns and down 
        // for rows.
        $originX = self::MARGIN_LEFT 
            + ($column * (self::LABEL_WIDTH + self::HORIZONTAL_SPACING));
        $originY = self::PAGE_HEIGHT - self::MARGIN_TOP 
            - (($row + 1) * (self::LABEL_HEIGHT + self::VERTICAL_SPACING));
        
        $page->saveGS();
        
        // Clip on label boundaries to stop text from running over.
        $page->clipRectangle(
            $originX, 
            $originY, 
            $originX + self::LABEL_WIDTH, 
            $originY + self::LABEL_HEIGHT
        );
        
//Places QR Code Image (original)

        $image = $this->_getQrCode($this->_baseUrl . '/items/show/' . $item->id);
        $page->drawImage(
            $image, 
            $originX, 
            $originY, 
            $originX + self::LABEL_HEIGHT, 
            $originY + self::LABEL_HEIGHT
        );

//Places Title next to QR Code (original)

        $titles = $item->getElementTexts('Dublin Core', 'Title');
        if (count($titles) > 0) {
            $textOriginX = $originX + self::LABEL_HEIGHT;
            $textOriginY = $originY + (0.8 * self::LABEL_HEIGHT) ;
            $cleanTitle = strip_tags(htmlspecialchars_decode($titles[0]->text));
            $this->_drawWrappedText(
                $page,
                $cleanTitle, 
                $textOriginX, 
                $textOriginY, 
                self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
            );   
        }

//Prints Creator under Title 

        $creators = $item->getElementTexts('Dublin Core', 'Creator');
        if (count($creators) > 0) {
            $textOriginX = $originX + self::LABEL_HEIGHT;
            $textOriginY = $originY + (0.6 * self::LABEL_HEIGHT) ;
            $cleanCreator = strip_tags(htmlspecialchars_decode($creators[0]->text));
            $this->_drawWrappedText(
                $page,
                $cleanCreator, 
                $textOriginX, 
                $textOriginY, 
                self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
            );   
        }

// //Prints ID under Creator 
$textOriginX = $originX + self::LABEL_HEIGHT;
$textOriginY = $originY + (0.4 * self::LABEL_HEIGHT) ;
$this->drawWrappedText(
        $page, 
        (string) $item->id, 
        $textOriginX, 
        $textOriginY, 
        self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
);

        // Remove clipping rectangle
        $page->restoreGS();
        
        // Release objects after use to keep memory usage down
        release_object($item);
    }

    private function _addItems($items, $filePath) {
        // To conserve memory on big jobs, the PDF should be saved 
        // incrementally after the initial page has been added. This has the 
        // additional side effect of producing a partial report in the event
        // of an error.
        $updateOnly = true;
        $itemsPerPage = self::ROWS * self::COLUMNS;

        // Reloading the PDF file (as opposed to reusing the initial 
        // object) also saves on memory, albeit inexplicably so.
        $pdf = Zend_Pdf::load($filePath);
        // Split the array into groups with just enough to fit on each 
        // page.
        $itemChunks = array_chunk($items, $itemsPerPage);
        foreach ($itemChunks as $chunk) {
            _log("Adding page for a new chunk.");
            $page = $this->_addPage($pdf);
            $column = 0;
            $row = 0;
            foreach ($chunk as $item) {
                $this->_drawItemLabel($page, $column, $row, $item);
                $row++;

                if($row >= self::ROWS) {
                    $column++;
                    $row = 0;
                }
                if($column >= self::COLUMNS) {
                    $column = 0;
                }
            }
        }
        $pdf->save($filePath, $updateOnly);
        //_log(memory_get_peak_usage());
        _log(memory_get_usage(true));
    }
    
    /**
     * Adds a new page to the PDF document.
     *
     * @return Zend_Pdf_Page
     */
    private function _addPage(Zend_Pdf $pdf)
    {
        $newPage = $pdf->newPage(Zend_Pdf_Page::SIZE_LETTER);
        $newPage->setFont($this->_font, self::FONT_SIZE);
        $pdf->pages[] = $newPage;
        $this->_pdfPageCount++;
        return $newPage;
    }
    
    /**
     * Wraps text on word boundaries and draws resulting text in consecutive
     * lines down from the origin point.
     *
     * @param string $text Text to draw
     * @param int $x X coordinate of origin on page
     * @param int $y Y coordinate of origin on page
     * @param int $wrapWidth Maximum width of a line
     */
    private function _drawWrappedText(
        Zend_Pdf_Page $page, 
        $text, 
        $x, 
        $y, 
        $wrapWidth
    ) {
        $wrappedText = $this->_wrapText($text, $wrapWidth);
        $lines = explode("\n", $wrappedText);
        foreach($lines as $line)
        {
            $page->drawText($line, $x, $y);
            $y -= self::FONT_SIZE + 5;
        }
    }
    
    /**
     * Returns text with newlines given a maximum width in points.
     *
     * @param string $text Text to wrap
     * @param int $wrapWidth Maximum width of a line
     * @return string Original text with newline characters inserted
     */
    private function _wrapText($text, $wrapWidth)
    {
        $wrappedText = '';
        $words = explode(' ', $text);
        $wrappedLine = '';
        foreach ($words as $word)
        {
            // if adding a new word isn't wider than $wrapWidth, add the
            // word to the line
            $wrappedWord = empty($wrappedLine) ? $word : " $word";
            $stringWidth = $this->_widthForStringUsingFontSize(
                $wrappedLine . $wrappedWord, 
                $this->_font, 
                self::FONT_SIZE
            );
            if ($stringWidth < $wrapWidth) {
                $wrappedLine .= $wrappedWord;
            } else {
                if (empty($wrappedLine)) {
                    $wrappedText .= "$word\n";
                    $wrappedLine = ''; 
                } else {
                    $wrappedText .= "$wrappedLine\n";
                    $wrappedLine = $word;
                }
            }
        }
        $wrappedText .= $wrappedLine;
        return $wrappedText;
    }
    
    /**
     * Returns the total width in points of the string using the specified
     * font and size.
     *
     * @link http://devzone.zend.com/article/2525
     * @param string $string
     * @param Zend_Pdf_Resource_Font $font
     * @param float $fontSize Font size in points
     * @return float
     */
    private function _widthForStringUsingFontSize($string, $font, $fontSize)
    {
        $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string);
        $characters = array();
        for ($i = 0; $i < strlen($drawingString); $i++) {
            $characters[] = (ord($drawingString[$i++]) << 8) |
                             ord($drawingString[$i]);
        }
        $glyphs = $font->glyphNumbersForCharacters($characters);
        $widths = $font->widthsForGlyphs($glyphs);
        $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
        return $stringWidth;
    }

    private function _getQrCode($uri)
    {
        if (!$this->_qrGenerator) {
            $this->_qrGenerator = new Reports_Generator_PdfQrCode_QuickCharts(
                self::QR_WIDTH,
                self::QR_HEIGHT
            );
        }
        return $this->_qrGenerator->generate($uri);
    }

    /**
     * Returns the readable name of this output format.
     *
     * @return string Human-readable name for output format
     */
    public static function getReadableName() {
        return 'QR Code (PDF)';
    }
    
    /**
     * Returns the HTTP content type to declare for the output format.
     *
     * @return string HTTP Content-type
     */
    public function getContentType() {
        return 'application/pdf';
    }
    
    /**
     * Returns the file extension to append to the generated report.
     *
     * @return string File extension
     */
    public function getExtension() {
        return 'zip';
    }
}

Well, there doesn’t seem to be anything obviously wrong there to my eyes.

Is it really that change that’s making the difference (so, if you just comment those lines out, does the import work, and then if you uncomment them again, does it break again)?

hey @jflatnes, commented out lines , commented that back in - after fighting some syntax errors i can verify that it works when commented out, fails and spins in progress when uncommented.

These are the lines (250-261 in this file) in question:

// //Prints ID under Creator 
 {
 $textOriginX = $originX + self::LABEL_HEIGHT;
 $textOriginY = $originY + (0.4 * self::LABEL_HEIGHT) ;
 $this->drawWrappedText(
       	$page, 
       	(string) $item->id, 
		$textOriginX, 
		$textOriginY, 
        self::LABEL_WIDTH - (self::LABEL_HEIGHT + 4)
		);
 		}

at a loss, any help would be appreciated

OK, I’ve finally had time to look at this and it’s a very simple issue: you’re missing an underscore at the start of the function name _drawWrappedText. Compare that same call in the non-working section with the previous ones: they have the underscore and this one doesn’t. I didn’t check carefully and just pulled it over with the same typo from your original code sample when suggesting what you should use instead.

1 Like

Hey @jflatnes Thank you for following up on this, that fixed the hanging issue. The PDF is now generated as expected.

In case anyone else stumbles on this thread - the functioning PdfQrCode.php is available on GitHub @ https://github.com/cachatj/PdfLabel/blob/master/PdfQrCode.php

Now I am attempting to modify the Text (make title bold, center itemID etc) - if you have any insight there, would appreciate any direction. It looks like _drawWrappedText uses ZendPDF and I can modify placement/formatting there. (Lines 321 - 417)

You can do that kind of thing, but Zend_Pdf is quite low-level, so things like centering and so on require you to “do the math,” so to speak. Bolding requires you to set the font to the bold variant, i.e. FONT_HELVETICA_BOLD instead of FONT_HELVETICA.

This topic was automatically closed 250 days after the last reply. New replies are no longer allowed.