*/
abstract class Mage_Sales_Model_Order_Pdf_Abstract extends Varien_Object
{
/**
* Y coordinate
*
* @var int
*/
public $y;
/**
* Item renderers with render type key
*
* model => the model name
* renderer => the renderer model
*
* @var array
*/
protected $_renderers = array();
/**
* Predefined constants
*/
const XML_PATH_SALES_PDF_INVOICE_PUT_ORDER_ID = 'sales_pdf/invoice/put_order_id';
const XML_PATH_SALES_PDF_SHIPMENT_PUT_ORDER_ID = 'sales_pdf/shipment/put_order_id';
const XML_PATH_SALES_PDF_CREDITMEMO_PUT_ORDER_ID = 'sales_pdf/creditmemo/put_order_id';
/**
* Zend PDF object
*
* @var Zend_Pdf
*/
protected $_pdf;
/**
* Default total model
*
* @var string
*/
protected $_defaultTotalModel = 'sales/order_pdf_total_default';
/**
* Retrieve PDF
*
* @return Zend_Pdf
*/
abstract public function getPdf();
/**
* Returns the total width in points of the string using the specified font and
* size.
*
* This is not the most efficient way to perform this calculation. I'm
* concentrating optimization efforts on the upcoming layout manager class.
* Similar calculations exist inside the layout manager class, but widths are
* generally calculated only after determining line fragments.
*
* @param string $string
* @param Zend_Pdf_Resource_Font $font
* @param float $fontSize Font size in points
* @return float
*/
public function widthForStringUsingFontSize($string, $font, $fontSize)
{
$drawingString = '"libiconv"' == ICONV_IMPL ?
iconv('UTF-8', 'UTF-16BE//IGNORE', $string) :
@iconv('UTF-8', 'UTF-16BE', $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;
}
/**
* Calculate coordinates to draw something in a column aligned to the right
*
* @param string $string
* @param int $x
* @param int $columnWidth
* @param Zend_Pdf_Resource_Font $font
* @param int $fontSize
* @param int $padding
* @return int
*/
public function getAlignRight($string, $x, $columnWidth, Zend_Pdf_Resource_Font $font, $fontSize, $padding = 5)
{
$width = $this->widthForStringUsingFontSize($string, $font, $fontSize);
return $x + $columnWidth - $width - $padding;
}
/**
* Calculate coordinates to draw something in a column aligned to the center
*
* @param string $string
* @param int $x
* @param int $columnWidth
* @param Zend_Pdf_Resource_Font $font
* @param int $fontSize
* @return int
*/
public function getAlignCenter($string, $x, $columnWidth, Zend_Pdf_Resource_Font $font, $fontSize)
{
$width = $this->widthForStringUsingFontSize($string, $font, $fontSize);
return $x + round(($columnWidth - $width) / 2);
}
/**
* Insert logo to pdf page
*
* @param Zend_Pdf_Page $page
* @param null $store
*/
protected function insertLogo(&$page, $store = null)
{
$this->y = $this->y ? $this->y : 815;
$image = Mage::getStoreConfig('sales/identity/logo', $store);
if ($image) {
$image = Mage::getBaseDir('media') . '/sales/store/logo/' . $image;
if (is_file($image)) {
$image = Zend_Pdf_Image::imageWithPath($image);
$top = 830; //top border of the page
$widthLimit = 270; //half of the page width
$heightLimit = 270; //assuming the image is not a "skyscraper"
$width = $image->getPixelWidth();
$height = $image->getPixelHeight();
//preserving aspect ratio (proportions)
$ratio = $width / $height;
if ($ratio > 1 && $width > $widthLimit) {
$width = $widthLimit;
$height = $width / $ratio;
} elseif ($ratio < 1 && $height > $heightLimit) {
$height = $heightLimit;
$width = $height * $ratio;
} elseif ($ratio == 1 && $height > $heightLimit) {
$height = $heightLimit;
$width = $widthLimit;
}
$y1 = $top - $height;
$y2 = $top;
$x1 = 25;
$x2 = $x1 + $width;
//coordinates after transformation are rounded by Zend
$page->drawImage($image, $x1, $y1, $x2, $y2);
$this->y = $y1 - 10;
}
}
}
/**
* Insert address to pdf page
*
* @param Zend_Pdf_Page $page
* @param null $store
*/
protected function insertAddress(&$page, $store = null)
{
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
$font = $this->_setFontRegular($page, 10);
$page->setLineWidth(0);
$this->y = $this->y ? $this->y : 815;
$top = 815;
foreach (explode("\n", Mage::getStoreConfig('sales/identity/address', $store)) as $value){
if ($value !== '') {
$value = preg_replace('/
]*>/i', "\n", $value);
foreach (Mage::helper('core/string')->str_split($value, 45, true, true) as $_value) {
$page->drawText(trim(strip_tags($_value)),
$this->getAlignRight($_value, 130, 440, $font, 10),
$top,
'UTF-8');
$top -= 10;
}
}
}
$this->y = ($this->y > $top) ? $top : $this->y;
}
/**
* Format address
*
* @param string $address
* @return array
*/
protected function _formatAddress($address)
{
$return = array();
foreach (explode('|', $address) as $str) {
foreach (Mage::helper('core/string')->str_split($str, 45, true, true) as $part) {
if (empty($part)) {
continue;
}
$return[] = $part;
}
}
return $return;
}
/**
* Calculate address height
*
* @param array $address
* @return int Height
*/
protected function _calcAddressHeight($address)
{
$y = 0;
foreach ($address as $value){
if ($value !== '') {
$text = array();
foreach (Mage::helper('core/string')->str_split($value, 55, true, true) as $_value) {
$text[] = $_value;
}
foreach ($text as $part) {
$y += 15;
}
}
}
return $y;
}
/**
* Insert order to pdf page
*
* @param Zend_Pdf_Page $page
* @param Mage_Sales_Model_Order $obj
* @param bool $putOrderId
*/
protected function insertOrder(&$page, $obj, $putOrderId = true)
{
if ($obj instanceof Mage_Sales_Model_Order) {
$shipment = null;
$order = $obj;
} elseif ($obj instanceof Mage_Sales_Model_Order_Shipment) {
$shipment = $obj;
$order = $shipment->getOrder();
}
$this->y = $this->y ? $this->y : 815;
$top = $this->y;
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0.45));
$page->setLineColor(new Zend_Pdf_Color_GrayScale(0.45));
$page->drawRectangle(25, $top, 570, $top - 55);
$page->setFillColor(new Zend_Pdf_Color_GrayScale(1));
$this->setDocHeaderCoordinates(array(25, $top, 570, $top - 55));
$this->_setFontRegular($page, 10);
if ($putOrderId) {
$page->drawText(
Mage::helper('sales')->__('Order # ') . $order->getRealOrderId(), 35, ($top -= 30), 'UTF-8'
);
}
$page->drawText(
Mage::helper('sales')->__('Order Date: ') . Mage::helper('core')->formatDate(
$order->getCreatedAtStoreDate(), 'medium', false
),
35,
($top -= 15),
'UTF-8'
);
$top -= 10;
$page->setFillColor(new Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92));
$page->setLineColor(new Zend_Pdf_Color_GrayScale(0.5));
$page->setLineWidth(0.5);
$page->drawRectangle(25, $top, 275, ($top - 25));
$page->drawRectangle(275, $top, 570, ($top - 25));
/* Calculate blocks info */
/* Billing Address */
$billingAddress = $this->_formatAddress($order->getBillingAddress()->format('pdf'));
/* Payment */
$paymentInfo = Mage::helper('payment')->getInfoBlock($order->getPayment())
->setIsSecureMode(true)
->toPdf();
$paymentInfo = htmlspecialchars_decode($paymentInfo, ENT_QUOTES);
$payment = explode('{{pdf_row_separator}}', $paymentInfo);
foreach ($payment as $key=>$value){
if (strip_tags(trim($value)) == '') {
unset($payment[$key]);
}
}
reset($payment);
/* Shipping Address and Method */
if (!$order->getIsVirtual()) {
/* Shipping Address */
$shippingAddress = $this->_formatAddress($order->getShippingAddress()->format('pdf'));
$shippingMethod = $order->getShippingDescription();
}
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
$this->_setFontBold($page, 12);
$page->drawText(Mage::helper('sales')->__('Sold to:'), 35, ($top - 15), 'UTF-8');
if (!$order->getIsVirtual()) {
$page->drawText(Mage::helper('sales')->__('Ship to:'), 285, ($top - 15), 'UTF-8');
} else {
$page->drawText(Mage::helper('sales')->__('Payment Method:'), 285, ($top - 15), 'UTF-8');
}
$addressesHeight = $this->_calcAddressHeight($billingAddress);
if (isset($shippingAddress)) {
$addressesHeight = max($addressesHeight, $this->_calcAddressHeight($shippingAddress));
}
$page->setFillColor(new Zend_Pdf_Color_GrayScale(1));
$page->drawRectangle(25, ($top - 25), 570, $top - 33 - $addressesHeight);
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
$this->_setFontRegular($page, 10);
$this->y = $top - 40;
$addressesStartY = $this->y;
foreach ($billingAddress as $value){
if ($value !== '') {
$text = array();
foreach (Mage::helper('core/string')->str_split($value, 45, true, true) as $_value) {
$text[] = $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 35, $this->y, 'UTF-8');
$this->y -= 15;
}
}
}
$addressesEndY = $this->y;
if (!$order->getIsVirtual()) {
$this->y = $addressesStartY;
foreach ($shippingAddress as $value){
if ($value!=='') {
$text = array();
foreach (Mage::helper('core/string')->str_split($value, 45, true, true) as $_value) {
$text[] = $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 285, $this->y, 'UTF-8');
$this->y -= 15;
}
}
}
$addressesEndY = min($addressesEndY, $this->y);
$this->y = $addressesEndY;
$page->setFillColor(new Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92));
$page->setLineWidth(0.5);
$page->drawRectangle(25, $this->y, 275, $this->y-25);
$page->drawRectangle(275, $this->y, 570, $this->y-25);
$this->y -= 15;
$this->_setFontBold($page, 12);
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
$page->drawText(Mage::helper('sales')->__('Payment Method'), 35, $this->y, 'UTF-8');
$page->drawText(Mage::helper('sales')->__('Shipping Method:'), 285, $this->y , 'UTF-8');
$this->y -=10;
$page->setFillColor(new Zend_Pdf_Color_GrayScale(1));
$this->_setFontRegular($page, 10);
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
$paymentLeft = 35;
$yPayments = $this->y - 15;
}
else {
$yPayments = $addressesStartY;
$paymentLeft = 285;
}
foreach ($payment as $value){
if (trim($value) != '') {
//Printing "Payment Method" lines
$value = preg_replace('/
]*>/i', "\n", $value);
foreach (Mage::helper('core/string')->str_split($value, 45, true, true) as $_value) {
$page->drawText(strip_tags(trim($_value)), $paymentLeft, $yPayments, 'UTF-8');
$yPayments -= 15;
}
}
}
if ($order->getIsVirtual()) {
// replacement of Shipments-Payments rectangle block
$yPayments = min($addressesEndY, $yPayments);
$page->drawLine(25, ($top - 25), 25, $yPayments);
$page->drawLine(570, ($top - 25), 570, $yPayments);
$page->drawLine(25, $yPayments, 570, $yPayments);
$this->y = $yPayments - 15;
} else {
$topMargin = 15;
$methodStartY = $this->y;
$this->y -= 15;
foreach (Mage::helper('core/string')->str_split($shippingMethod, 45, true, true) as $_value) {
$page->drawText(strip_tags(trim($_value)), 285, $this->y, 'UTF-8');
$this->y -= 15;
}
$yShipments = $this->y;
$totalShippingChargesText = "(" . Mage::helper('sales')->__('Total Shipping Charges') . " "
. $order->formatPriceTxt($order->getShippingAmount()) . ")";
$page->drawText($totalShippingChargesText, 285, $yShipments - $topMargin, 'UTF-8');
$yShipments -= $topMargin + 10;
$tracks = array();
if ($shipment) {
$tracks = $shipment->getAllTracks();
}
if (count($tracks)) {
$page->setFillColor(new Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92));
$page->setLineWidth(0.5);
$page->drawRectangle(285, $yShipments, 510, $yShipments - 10);
$page->drawLine(400, $yShipments, 400, $yShipments - 10);
//$page->drawLine(510, $yShipments, 510, $yShipments - 10);
$this->_setFontRegular($page, 9);
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0));
//$page->drawText(Mage::helper('sales')->__('Carrier'), 290, $yShipments - 7 , 'UTF-8');
$page->drawText(Mage::helper('sales')->__('Title'), 290, $yShipments - 7, 'UTF-8');
$page->drawText(Mage::helper('sales')->__('Number'), 410, $yShipments - 7, 'UTF-8');
$yShipments -= 20;
$this->_setFontRegular($page, 8);
foreach ($tracks as $track) {
$CarrierCode = $track->getCarrierCode();
if ($CarrierCode != 'custom') {
$carrier = Mage::getSingleton('shipping/config')->getCarrierInstance($CarrierCode);
$carrierTitle = $carrier->getConfigData('title');
} else {
$carrierTitle = Mage::helper('sales')->__('Custom Value');
}
//$truncatedCarrierTitle = substr($carrierTitle, 0, 35) . (strlen($carrierTitle) > 35 ? '...' : '');
$maxTitleLen = 45;
$endOfTitle = strlen($track->getTitle()) > $maxTitleLen ? '...' : '';
$truncatedTitle = substr($track->getTitle(), 0, $maxTitleLen) . $endOfTitle;
//$page->drawText($truncatedCarrierTitle, 285, $yShipments , 'UTF-8');
$page->drawText($truncatedTitle, 292, $yShipments , 'UTF-8');
$page->drawText($track->getNumber(), 410, $yShipments , 'UTF-8');
$yShipments -= $topMargin - 5;
}
} else {
$yShipments -= $topMargin - 5;
}
$currentY = min($yPayments, $yShipments);
// replacement of Shipments-Payments rectangle block
$page->drawLine(25, $methodStartY, 25, $currentY); //left
$page->drawLine(25, $currentY, 570, $currentY); //bottom
$page->drawLine(570, $currentY, 570, $methodStartY); //right
$this->y = $currentY;
$this->y -= 15;
}
}
/**
* Insert title and number for concrete document type
*
* @param Zend_Pdf_Page $page
* @param string $text
* @return void
*/
public function insertDocumentNumber(Zend_Pdf_Page $page, $text)
{
$page->setFillColor(new Zend_Pdf_Color_GrayScale(1));
$this->_setFontRegular($page, 10);
$docHeader = $this->getDocHeaderCoordinates();
$page->drawText($text, 35, $docHeader[1] - 15, 'UTF-8');
}
/**
* Sort totals list
*
* @param array $a
* @param array $b
* @return int
*/
protected function _sortTotalsList($a, $b) {
if (!isset($a['sort_order']) || !isset($b['sort_order'])) {
return 0;
}
if ($a['sort_order'] == $b['sort_order']) {
return 0;
}
return ($a['sort_order'] > $b['sort_order']) ? 1 : -1;
}
/**
* Return total list
*
* @param Mage_Sales_Model_Abstract $source
* @return array
*/
protected function _getTotalsList($source)
{
$totals = Mage::getConfig()->getNode('global/pdf/totals')->asArray();
usort($totals, array($this, '_sortTotalsList'));
$totalModels = array();
foreach ($totals as $index => $totalInfo) {
if (!empty($totalInfo['model'])) {
$totalModel = Mage::getModel($totalInfo['model']);
if ($totalModel instanceof Mage_Sales_Model_Order_Pdf_Total_Default) {
$totalInfo['model'] = $totalModel;
} else {
Mage::throwException(
Mage::helper('sales')->__('PDF total model should extend Mage_Sales_Model_Order_Pdf_Total_Default')
);
}
} else {
$totalModel = Mage::getModel($this->_defaultTotalModel);
}
$totalModel->setData($totalInfo);
$totalModels[] = $totalModel;
}
return $totalModels;
}
/**
* Insert totals to pdf page
*
* @param Zend_Pdf_Page $page
* @param Mage_Sales_Model_Abstract $source
* @return Zend_Pdf_Page
*/
protected function insertTotals($page, $source){
$order = $source->getOrder();
$totals = $this->_getTotalsList($source);
$lineBlock = array(
'lines' => array(),
'height' => 15
);
foreach ($totals as $total) {
$total->setOrder($order)
->setSource($source);
if ($total->canDisplay()) {
$total->setFontSize(10);
foreach ($total->getTotalsForDisplay() as $totalData) {
$lineBlock['lines'][] = array(
array(
'text' => $totalData['label'],
'feed' => 475,
'align' => 'right',
'font_size' => $totalData['font_size'],
'font' => 'bold'
),
array(
'text' => $totalData['amount'],
'feed' => 565,
'align' => 'right',
'font_size' => $totalData['font_size'],
'font' => 'bold'
),
);
}
}
}
$this->y -= 20;
$page = $this->drawLineBlocks($page, array($lineBlock));
return $page;
}
/**
* Parse item description
*
* @param Varien_Object $item
* @return array
*/
protected function _parseItemDescription($item)
{
$matches = array();
$description = $item->getDescription();
if (preg_match_all('/