You are here

collageformatter.module in Collage Formatter 7

Same filename and directory in other branches
  1. 8 collageformatter.module

Main file for Collage Formatter module.

File

collageformatter.module
View source
<?php

/**
 * @file
 * Main file for Collage Formatter module.
 */

/**
 * Implements hook_field_formatter_info().
 */
function collageformatter_field_formatter_info() {
  return array(
    'collageformatter' => array(
      'label' => t('Collage'),
      'field types' => array(
        'image',
      ),
      'settings' => _collageformatter_default_settings(),
      'description' => t('Display multi-value image fields as collage.'),
    ),
  );
}

/**
 * Default Collage Formatter settings.
 */
function _collageformatter_default_settings() {
  return array(
    'collage_number' => 1,
    'images_per_collage' => NULL,
    'images_to_skip' => 0,
    'collage_orientation' => 0,
    'collage_width' => 500,
    'collage_height' => '',
    'collage_border_size' => 0,
    'collage_border_color' => '#ffffff',
    'gap_size' => 0,
    'gap_color' => '#ffffff',
    'border_size' => 0,
    'border_color' => '#000000',
    'image_link' => 'file',
    'image_link_image_style' => NULL,
    'image_link_modal' => NULL,
    'image_link_class' => NULL,
    'image_link_rel' => NULL,
    'generate_image_derivatives' => 0,
    'prevent_upscale' => 0,
    'advanced' => array(
      'original_image_reference' => 'symlink',
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function collageformatter_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $element = _collageformatter_settings_form($settings);
  $element['image_link_image_style']['#states'] = array(
    'visible' => array(
      ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][image_link]"]' => array(
        'value' => 'file',
      ),
    ),
  );
  $element['image_link_modal']['#states'] = array(
    'visible' => array(
      ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][image_link]"]' => array(
        'value' => 'file',
      ),
    ),
  );
  $element['image_link_class']['#states'] = array(
    'invisible' => array(
      ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][image_link]"]' => array(
        'value' => '',
      ),
    ),
  );
  $element['image_link_rel']['#states'] = array(
    'invisible' => array(
      ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][image_link]"]' => array(
        'value' => '',
      ),
    ),
  );

  // Field formatter in Views - doesn't work.
  if ($view_mode == '_custom') {
    $element['image_link_image_style']['#states'] = array(
      'visible' => array(
        ':input[name="options[settings][image_link]"]' => array(
          'value' => 'file',
        ),
      ),
    );
    $element['image_link_modal']['#states'] = array(
      'visible' => array(
        ':input[name="options[settings][image_link]"]' => array(
          'value' => 'file',
        ),
      ),
    );
    $element['image_link_class']['#states'] = array(
      'invisible' => array(
        ':input[name="options[settings][image_link]"]' => array(
          'value' => '',
        ),
      ),
    );
    $element['image_link_rel']['#states'] = array(
      'invisible' => array(
        ':input[name="options[settings][image_link]"]' => array(
          'value' => '',
        ),
      ),
    );
  }
  return $element;
}

/**
 * Settings form for Collage Formatter.
 */
function _collageformatter_settings_form($settings) {
  $options_0_50 = drupal_map_assoc(range(0, 50));
  $options_1_50 = drupal_map_assoc(range(1, 50));
  $element['collage_number'] = array(
    '#type' => 'select',
    '#title' => t('Number of collages'),
    '#title_display' => 'invisible',
    '#options' => $options_1_50,
    '#default_value' => $settings['collage_number'],
    '#field_prefix' => t('Generate'),
    '#field_suffix' => t('collage(s)'),
    '#prefix' => '<div class="container-inline">',
  );
  $element['images_per_collage'] = array(
    '#type' => 'select',
    '#title' => t('Images per collage'),
    '#title_display' => 'invisible',
    '#options' => $options_1_50,
    '#default_value' => $settings['images_per_collage'],
    '#empty_option' => t('all'),
    '#field_prefix' => t('with'),
    '#field_suffix' => t('image(s) per collage') . ';',
  );
  $element['images_to_skip'] = array(
    '#type' => 'select',
    '#title' => t('Images to skip'),
    '#title_display' => 'invisible',
    '#options' => $options_0_50,
    '#default_value' => $settings['images_to_skip'],
    '#field_prefix' => t('Skip'),
    '#field_suffix' => t('image(s) from the start'),
    '#suffix' => '</div>',
  );
  $element['collage_orientation'] = array(
    '#type' => 'select',
    '#title' => t('Collage orientation'),
    '#description' => t('Select if it should be a wide collage (landscape) or a tall one (portrait).'),
    '#options' => array(
      '0' => t('Landscape'),
      '1' => t('Portrait'),
    ),
    '#default_value' => $settings['collage_orientation'],
    '#prefix' => '<div class="container-inline">',
    '#field_suffix' => '</br>',
    '#suffix' => '</div>',
  );
  $element['collage_width'] = array(
    '#type' => 'textfield',
    '#title' => t('Collage width'),
    '#title_display' => 'invisible',
    '#default_value' => $settings['collage_width'],
    '#element_validate' => array(
      'element_validate_integer_positive',
    ),
    '#size' => 4,
    '#maxlength' => 4,
    '#prefix' => '<div class="container-inline"><strong>' . t('Collage width & height') . '</strong>',
    // '#field_prefix' => '</br>',
    '#field_suffix' => 'x',
  );
  $element['collage_height'] = array(
    '#type' => 'textfield',
    '#title' => t('Collage height'),
    '#title_display' => 'invisible',
    '#description' => t('Total collage width and height with all the borders and gaps. If you specify both then the images will be cropped.'),
    '#default_value' => $settings['collage_height'],
    '#element_validate' => array(
      'element_validate_integer_positive',
    ),
    '#size' => 4,
    '#maxlength' => 4,
    '#field_suffix' => 'px</br>',
    '#suffix' => '</div>',
  );
  $element['collage_border_size'] = array(
    '#type' => 'select',
    '#title' => t('Collage border'),
    '#options' => $options_0_50,
    '#default_value' => $settings['collage_border_size'],
    '#field_suffix' => 'px',
    '#prefix' => '<div class="container-inline">',
  );
  $element['collage_border_color'] = array(
    '#type' => 'textfield',
    '#title' => t('Collage border color'),
    '#default_value' => $settings['collage_border_color'],
    '#size' => 7,
    '#maxlength' => 7,
    '#suffix' => '<div class="collageformatter-color-picker"></div>' . '</div>',
    '#attached' => array(
      'library' => array(
        array(
          'system',
          'farbtastic',
        ),
      ),
      'js' => array(
        drupal_get_path('module', 'collageformatter') . '/js/collageformatter.admin.js' => array(
          'type' => 'file',
        ),
      ),
    ),
  );
  $element['gap_size'] = $element['collage_border_size'];
  $element['gap_size']['#title'] = t('Image gap');
  $element['gap_size']['#default_value'] = $settings['gap_size'];
  $element['gap_color'] = $element['collage_border_color'];
  $element['gap_color']['#title'] = t('Image gap color');
  $element['gap_color']['#default_value'] = $settings['gap_color'];
  $element['border_size'] = $element['collage_border_size'];
  $element['border_size']['#title'] = t('Image border');
  $element['border_size']['#default_value'] = $settings['border_size'];
  $element['border_color'] = $element['collage_border_color'];
  $element['border_color']['#title'] = t('Image border color');
  $element['border_color']['#default_value'] = $settings['border_color'];
  $link_types = array(
    'content' => t('Content'),
    'file' => t('File'),
  );
  $element['image_link'] = array(
    '#title' => t('Link image to'),
    '#type' => 'select',
    '#default_value' => $settings['image_link'],
    '#empty_option' => t('Nothing'),
    '#options' => $link_types,
    '#prefix' => '<div class="container-inline">',
  );
  $image_styles = image_style_options(FALSE);
  $element['image_link_image_style'] = array(
    '#title' => t('Target image style'),
    '#type' => 'select',
    '#default_value' => $settings['image_link_image_style'],
    '#empty_option' => t('None (original image)'),
    '#options' => $image_styles,
  );
  $modal_options = array();
  if (module_exists('colorbox')) {
    $modal_options['colorbox'] = t('Colorbox');
  }
  if (module_exists('shadowbox')) {
    $modal_options['shadowbox'] = t('Shadowbox');
  }
  if (module_exists('fancybox')) {
    $modal_options['fancybox'] = t('fancyBox');
  }
  if (module_exists('photobox')) {
    $modal_options['photobox'] = t('Photobox');
  }
  if (module_exists('photoswipe')) {
    $modal_options['photoswipe'] = t('PhotoSwipe');
  }
  if (module_exists('lightbox2')) {
    $modal_options['lightbox2'] = t('Lightbox2');
  }
  $element['image_link_modal'] = array(
    '#title' => t('Modal gallery'),
    '#type' => 'select',
    '#default_value' => $settings['image_link_modal'],
    '#empty_option' => t('None'),
    '#options' => $modal_options,
    '#suffix' => '</div>',
  );
  $element['image_link_class'] = array(
    '#type' => 'textfield',
    '#title' => t('Image link class'),
    // '#description' => t('Custom class to add to all image links.'),
    '#default_value' => $settings['image_link_class'],
    '#size' => 30,
    '#prefix' => '<div class="container-inline">',
  );
  $element['image_link_rel'] = array(
    '#type' => 'textfield',
    '#title' => t('Image link rel'),
    // '#description' => t('Custom rel attribute to add to all image links.'),
    '#default_value' => $settings['image_link_rel'],
    '#size' => 30,
    '#suffix' => '</div>',
  );
  $element['generate_image_derivatives'] = array(
    '#type' => 'checkbox',
    '#title' => t('Generate image derivatives'),
    '#description' => t('Generate image derivatives used in the collage while rendering it, before displaying.'),
    '#default_value' => $settings['generate_image_derivatives'],
  );
  $element['prevent_upscale'] = array(
    '#type' => 'checkbox',
    '#title' => t('Prevent images upscaling'),
    '#description' => t('Generated collage dimensions might be smaller.'),
    '#default_value' => $settings['prevent_upscale'],
  );
  $element['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $element['advanced']['original_image_reference'] = array(
    '#type' => 'radios',
    '#title' => t('Original image reference method'),
    '#description' => t('If you need to add additional image effects to collageformatter image style before the "Collage Formatter" effect then you need to use "Symlink" or "Copy" method.'),
    '#options' => array(
      'symlink' => t('Symlink'),
      'fake' => t('Fake image'),
      'copy' => t('Copy'),
    ),
    '#default_value' => $settings['advanced']['original_image_reference'],
  );
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function collageformatter_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = array();
  $summary[] = t('Generate') . ' <strong>' . $settings['collage_number'] . '</strong> ' . t('collage(s)') . ' ' . t('with') . ' <strong>' . ($settings['images_per_collage'] ? $settings['images_per_collage'] : t('all')) . '</strong> ' . t('image(s) per collage') . '; ' . t('Skip') . ' <strong>' . $settings['images_to_skip'] . '</strong> ' . t('image(s) from the start');
  $summary[] = t('Collage orientation') . ': ' . ($settings['collage_orientation'] ? t('Portrait') : t('Landscape'));
  $summary[] = t('Collage width') . ': ' . ($settings['collage_width'] ? $settings['collage_width'] . 'px' : t('Not set'));
  $summary[] = t('Collage height') . ': ' . ($settings['collage_height'] ? $settings['collage_height'] . 'px' : t('Not set'));
  $summary[] = t('Collage border') . ': ' . $settings['collage_border_size'] . 'px <span style="background-color: ' . $settings['collage_border_color'] . ';">' . $settings['collage_border_color'] . '</span>';
  $summary[] = t('Image gap') . ': ' . $settings['gap_size'] . 'px <span style="background-color: ' . $settings['gap_color'] . ';">' . $settings['gap_color'] . '</span>';
  $summary[] = t('Image border') . ': ' . $settings['border_size'] . 'px <span style="background-color: ' . $settings['border_color'] . ';">' . $settings['border_color'] . '</span>';
  $link_types = array(
    'content' => t('Images linked to content'),
    'file' => t('Images linked to file'),
  );
  if (isset($link_types[$settings['image_link']])) {
    $summary[] = $link_types[$settings['image_link']];
    if ($settings['image_link'] == 'file') {
      if (empty($settings['image_link_image_style'])) {
        $summary[] = t('Target image style') . ': ' . t('None (original image)');
      }
      else {
        $image_styles = image_style_options(FALSE);
        $summary[] = t('Target image style') . ': ' . $image_styles[$settings['image_link_image_style']];
      }

      // Modal gallery summary.
      if (empty($settings['image_link_modal'])) {
        $summary[] = t('Modal gallery') . ': ' . t('None');
      }
      else {
        $summary[] = t('Modal gallery') . ': ' . $settings['image_link_modal'];
      }

      // Custom class/rel summary.
      $custom = array();
      if (!empty($settings['image_link_class'])) {
        $custom[] = 'class="' . check_plain($settings['image_link_class']) . '"';
      }
      if (!empty($settings['image_link_rel'])) {
        $custom[] = 'rel="' . check_plain($settings['image_link_rel']) . '"';
      }
      $summary[] = implode(' ', $custom);
    }
  }
  else {
    $summary[] = t('Images without links');
  }
  if ($settings['generate_image_derivatives']) {
    $summary[] = t('Generate image derivatives');
  }
  else {
    $summary[] = t('Do not generate image derivatives');
  }
  if ($settings['prevent_upscale']) {
    $summary[] = t('Prevent images upscaling');
  }
  return implode('<br />', $summary);
}

/**
 * Implements hook_field_formatter_view().
 */
function collageformatter_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {

  // If there are no images, don't do anything.
  if (empty($items)) {
    return '';
  }
  $settings = $display['settings'];
  $ids = entity_extract_ids($entity_type, $entity);
  $entity_id = $ids[0];
  $settings['gallery'] = 'collageformatter-' . $field['field_name'] . '-' . $entity_id;
  if ($settings['image_link'] == 'content') {
    $content_uri = entity_uri($entity_type, $entity);
    foreach ($items as &$item) {
      $item['content_uri'] = $content_uri['path'];
    }
  }
  $collage = collageformatter_render_collage($items, $settings);
  return $collage;
}

/**
 * Returns renderable array of collages.
 */
function collageformatter_render_collage($images, $settings) {
  $collage = array();

  // Remove images to skip.
  if ($settings['images_to_skip']) {
    $images = array_slice($images, $settings['images_to_skip']);
  }

  // Prepare images.
  foreach ($images as $delta => &$image) {
    if (!isset($image['width']) || !isset($image['height'])) {
      if ($image_info = image_get_info($image['uri'])) {
        $image += $image_info;
      }
    }
    $image += array(
      'box_type' => 'image',
      'delta' => $delta,
      'total_width' => $image['width'] + 2 * $settings['border_size'] + $settings['gap_size'],
      'total_height' => $image['height'] + 2 * $settings['border_size'] + $settings['gap_size'],
    );
  }

  // Determine number of collages and how many images per collage.
  $collage_number = $settings['collage_number'];
  $images_per_collage = $settings['images_per_collage'] ? $settings['images_per_collage'] : round(count($images) / $collage_number);

  // Generate collages.
  while ($collage_number > 0) {
    $collage_number--;

    // If last collage and all images option - take all images.
    if ($collage_number == 0 && !$settings['images_per_collage']) {
      $collage_images = $images;
    }
    else {
      $collage_images = array_slice($images, 0, $images_per_collage);

      // Update images array and set as the last collage if there are no more images.
      if (!($images = array_slice($images, $images_per_collage))) {
        $collage_number = 0;
      }
    }

    // Generate collage layout.
    $box = _collageformatter_layout_box($collage_images, $settings['collage_orientation']);

    // Scale the collage.
    if ($settings['collage_width']) {
      $box['parent_total_width'] = $settings['collage_width'] - 2 * $settings['collage_border_size'];
      $dimensions = array(
        'width' => $box['parent_total_width'] - $settings['gap_size'],
      );
      $box = _collageformatter_scale_box($box, $dimensions);
      $box['parent_total_height'] = $box['total_height'] + $settings['gap_size'];
    }
    elseif ($settings['collage_height']) {
      $box['parent_total_height'] = $settings['collage_height'] - 2 * $settings['collage_border_size'];
      $dimensions = array(
        'height' => $box['parent_total_height'] - $settings['gap_size'],
      );
      $box = _collageformatter_scale_box($box, $dimensions);
      $box['parent_total_width'] = $box['total_width'] + $settings['gap_size'];
    }
    else {
      $box['parent_total_width'] = $box['total_width'] + $settings['gap_size'];
      $box['parent_total_height'] = $box['total_height'] + $settings['gap_size'];
    }

    // Resize the collage if both with and height are set.
    if ($settings['collage_width'] && $settings['collage_height']) {
      $box['parent_total_width'] = $settings['collage_width'] - 2 * $settings['collage_border_size'];
      $box['parent_total_height'] = $settings['collage_height'] - 2 * $settings['collage_border_size'];
      $dimensions = array(
        'width' => $box['parent_total_width'] - $settings['gap_size'],
        'height' => $box['parent_total_height'] - $settings['gap_size'],
      );
      $box = _collageformatter_resize_box($box, $dimensions);
    }

    // Check for upscaled images and prevent upscaling.
    if ($settings['prevent_upscale']) {
      $scale = _collageformatter_upscaling_check($box, $settings);
      if ($scale < 1) {
        $dimensions = array(
          'width' => $scale * $box['total_width'],
        );
        $box = _collageformatter_scale_box($box, $dimensions);
        $box['parent_total_width'] = $box['total_width'] + $settings['gap_size'];
        $box['parent_total_height'] = $box['total_height'] + $settings['gap_size'];
      }
    }
    $collage_wrapper_style = array();
    $collage_wrapper_style[] = 'max-width: ' . round($box['parent_total_width'] + 2 * $settings['collage_border_size'] - 0.5) . 'px;';

    // $collage_wrapper_style[] = 'box-sizing: border-box; -webkit-box-sizing: border-box; -moz-box-sizing: border-box;';
    $collage_style = array();

    // $collage_style[] = 'box-sizing: border-box; -webkit-box-sizing: border-box; -moz-box-sizing: border-box;';
    if ($settings['collage_border_size']) {
      $border = 'border: ' . $settings['collage_border_size'] . 'px solid';
      $border .= $settings['collage_border_color'] ? ' ' . $settings['collage_border_color'] : '';
      $collage_style[] = $border . ';';
    }
    if ($settings['gap_color']) {
      $collage_style[] = 'background-color: ' . $settings['gap_color'] . ';';
    }
    $collage_wrapper_class = array(
      'collageformatter-collage-wrapper',
    );
    if ($settings['image_link_modal'] == 'photoswipe') {
      $collage_wrapper_class[] = 'photoswipe-gallery';
    }
    $collage[] = array(
      '#theme' => 'collageformatter_collage',
      '#collage' => _collageformatter_render_box($box, $settings),
      '#collage_wrapper_class' => implode(' ', $collage_wrapper_class),
      '#collage_wrapper_style' => implode(' ', $collage_wrapper_style),
      '#collage_style' => implode(' ', $collage_style),
      '#collage_bottom_style' => 'clear: both; margin-bottom: ' . 100 * ($settings['gap_size'] / round($box['parent_total_width'] - 0.5)) . '%',
    );
  }
  return $collage;
}

/**
 * Recursive function to build the layout.
 * * @param $type
 *   boolean - TRUE for portrait (horizontal contact - vertical box type);
 *             FALSE for landscape (vertical contact - horizontal box type).
 */
function _collageformatter_layout_box($images, $type) {
  $box = array();
  $count = count($images);
  if ($count >= 2) {
    $size1 = floor($count / 2);
    $size2 = $count - $size1;
    $images1 = array_slice($images, 0, $size1);
    $images2 = array_slice($images, $size1, $size2);
    $box = array(
      'box_type' => 'box',
      'box_orientation' => $type ? 'vertical' : 'horizontal',
      'pixel_check' => FALSE,
    );
    $box[1] = _collageformatter_layout_box($images1, !$type);
    $box[2] = _collageformatter_layout_box($images2, !$type);
    $box[1]['parent_box_orientation'] = $box[2]['parent_box_orientation'] = $box['box_orientation'];
    $box[1]['pixel_check'] = FALSE;
    $box[2]['pixel_check'] = TRUE;
    if ($type) {

      // Horizontal contact; vertical box type.
      $dimensions = array(
        'width' => $box[1]['total_width'],
      );
    }
    else {

      // Vertical contact; horizontal box type.
      $dimensions = array(
        'height' => $box[1]['total_height'],
      );
    }
    $box[2] = _collageformatter_scale_box($box[2], $dimensions);
    if ($type) {

      // Horizontal contact; vertical box type.
      $box['total_height'] = $box[1]['total_height'] + $box[2]['total_height'];
      $box['total_width'] = $box[1]['total_width'];
    }
    else {

      // Vertical contact; horizontal box type.
      $box['total_width'] = $box[1]['total_width'] + $box[2]['total_width'];
      $box['total_height'] = $box[1]['total_height'];
    }
    $box[1]['parent_total_width'] = $box[2]['parent_total_width'] = $box['total_width'];
    $box[1]['parent_total_height'] = $box[2]['parent_total_height'] = $box['total_height'];
    $box[1]['siblings_total_width'] = $box[2]['total_width'];
    $box[1]['siblings_total_height'] = $box[2]['total_height'];
    $box[2]['siblings_total_width'] = $box[1]['total_width'];
    $box[2]['siblings_total_height'] = $box[1]['total_height'];
  }
  elseif ($count == 1) {
    $box = array_pop($images);
    $box['pixel_check'] = FALSE;
  }
  return $box;
}

/**
 * Recursive function to scale the box using only one dimension.
 */
function _collageformatter_scale_box($box, $dimensions) {

  // If it is an image - just scale it (change dimensions).
  if ($box['box_type'] == 'image') {
    if (array_key_exists('width', $dimensions)) {
      $box['total_height'] = $dimensions['width'] / $box['total_width'] * $box['total_height'];
      $box['total_width'] = $dimensions['width'];
    }
    elseif (array_key_exists('height', $dimensions)) {
      $box['total_width'] = $dimensions['height'] / $box['total_height'] * $box['total_width'];
      $box['total_height'] = $dimensions['height'];
    }
    return $box;
  }

  // If it is a box - then it should consist of two box elements;
  // Determine sizes of elements and scale them.
  if (array_key_exists('width', $dimensions)) {

    // Vertical box type; horizontal contact.
    if ($box['box_orientation'] == 'vertical') {
      $dimensions1 = $dimensions2 = $dimensions;
    }
    elseif ($box['box_orientation'] == 'horizontal') {
      $dimensions1 = array(
        'width' => $box[1]['total_width'] / ($box[1]['total_width'] + $box[2]['total_width']) * $dimensions['width'],
      );
      $dimensions2 = array(
        'width' => $box[2]['total_width'] / ($box[1]['total_width'] + $box[2]['total_width']) * $dimensions['width'],
      );
    }
  }
  elseif (array_key_exists('height', $dimensions)) {

    // Vertical box type; horizontal contact.
    if ($box['box_orientation'] == 'vertical') {
      $dimensions1 = array(
        'height' => $box[1]['total_height'] / ($box[1]['total_height'] + $box[2]['total_height']) * $dimensions['height'],
      );
      $dimensions2 = array(
        'height' => $box[2]['total_height'] / ($box[1]['total_height'] + $box[2]['total_height']) * $dimensions['height'],
      );
    }
    elseif ($box['box_orientation'] == 'horizontal') {
      $dimensions1 = $dimensions2 = $dimensions;
    }
  }
  $box[1] = _collageformatter_scale_box($box[1], $dimensions1);
  $box[2] = _collageformatter_scale_box($box[2], $dimensions2);
  if ($box['box_orientation'] == 'vertical') {
    $box['total_height'] = $box[1]['total_height'] + $box[2]['total_height'];
    $box['total_width'] = $box[1]['total_width'];
  }
  elseif ($box['box_orientation'] == 'horizontal') {
    $box['total_width'] = $box[1]['total_width'] + $box[2]['total_width'];
    $box['total_height'] = $box[1]['total_height'];
  }
  $box[1]['parent_total_width'] = $box[2]['parent_total_width'] = $box['total_width'];
  $box[1]['parent_total_height'] = $box[2]['parent_total_height'] = $box['total_height'];
  $box[1]['siblings_total_width'] = $box[2]['total_width'];
  $box[1]['siblings_total_height'] = $box[2]['total_height'];
  $box[2]['siblings_total_width'] = $box[1]['total_width'];
  $box[2]['siblings_total_height'] = $box[1]['total_height'];
  return $box;
}

/**
 * Recursive function to resize the box.
 */
function _collageformatter_resize_box($box, $dimensions) {

  // If it is an image - just resize it (change dimensions).
  if ($box['box_type'] == 'image') {
    $box['total_width'] = $dimensions['width'];
    $box['total_height'] = $dimensions['height'];
    return $box;
  }

  // If it is a box - then it should consist of two box elements;
  // Determine sizes of elements and resize them.
  // Vertical box type; horizontal contact.
  if ($box['box_orientation'] == 'vertical') {
    $dimensions1 = array(
      'width' => $dimensions['width'],
      'height' => $box[1]['total_height'] / ($box[1]['total_height'] + $box[2]['total_height']) * $dimensions['height'],
    );
    $dimensions2 = array(
      'width' => $dimensions['width'],
      'height' => $box[2]['total_height'] / ($box[1]['total_height'] + $box[2]['total_height']) * $dimensions['height'],
    );
  }
  elseif ($box['box_orientation'] == 'horizontal') {
    $dimensions1 = array(
      'width' => $box[1]['total_width'] / ($box[1]['total_width'] + $box[2]['total_width']) * $dimensions['width'],
      'height' => $dimensions['height'],
    );
    $dimensions2 = array(
      'width' => $box[2]['total_width'] / ($box[1]['total_width'] + $box[2]['total_width']) * $dimensions['width'],
      'height' => $dimensions['height'],
    );
  }
  $box[1] = _collageformatter_resize_box($box[1], $dimensions1);
  $box[2] = _collageformatter_resize_box($box[2], $dimensions2);
  if ($box['box_orientation'] == 'vertical') {
    $box['total_height'] = $box[1]['total_height'] + $box[2]['total_height'];
    $box['total_width'] = $box[1]['total_width'];
  }
  elseif ($box['box_orientation'] == 'horizontal') {
    $box['total_width'] = $box[1]['total_width'] + $box[2]['total_width'];
    $box['total_height'] = $box[1]['total_height'];
  }
  $box[1]['parent_total_width'] = $box[2]['parent_total_width'] = $box['total_width'];
  $box[1]['parent_total_height'] = $box[2]['parent_total_height'] = $box['total_height'];
  $box[1]['siblings_total_width'] = $box[2]['total_width'];
  $box[1]['siblings_total_height'] = $box[2]['total_height'];
  $box[2]['siblings_total_width'] = $box[1]['total_width'];
  $box[2]['siblings_total_height'] = $box[1]['total_height'];
  return $box;
}

/**
 * Recursive function to render the box.
 */
function _collageformatter_render_box($box, $settings) {
  $output = '';

  // Check if parent dimensions changed - and change yourself.
  if (array_key_exists('parent_box_orientation', $box)) {
    if ($box['parent_box_orientation'] == 'vertical') {
      $box['total_width'] = $box['parent_total_width'];
    }
    elseif ($box['parent_box_orientation'] == 'horizontal') {
      $box['total_height'] = $box['parent_total_height'];
    }
  }

  // Perform pixel check.
  if ($box['pixel_check']) {
    if ($box['parent_box_orientation'] == 'vertical') {
      $pixels = round($box['parent_total_height'] - 0.5) - round($box['total_height'] - 0.5) - round($box['siblings_total_height'] - 0.5);
      if ($pixels) {
        $box['total_height'] += $pixels;
      }
    }
    elseif ($box['parent_box_orientation'] == 'horizontal') {
      $pixels = round($box['parent_total_width'] - 0.5) - round($box['total_width'] - 0.5) - round($box['siblings_total_width'] - 0.5);
      if ($pixels) {
        $box['total_width'] += $pixels;
      }
    }
  }

  // Ensure that children have correct parent dimensions.
  if ($box['box_type'] == 'box') {
    $box[1]['parent_total_height'] = $box[2]['parent_total_height'] = $box['total_height'];
    $box[1]['parent_total_width'] = $box[2]['parent_total_width'] = $box['total_width'];
  }
  if ($box['box_type'] == 'box') {
    $box_style = array(
      'float: left;',
      'max-width: ' . round($box['total_width'] - 0.5) . 'px;',
    );
    $box_style[] = 'width: ' . 100 * (round($box['total_width'] - 0.5) / round($box['parent_total_width'] - 0.5)) . '%;';
    $content[] = _collageformatter_render_box($box[1], $settings);
    $content[] = _collageformatter_render_box($box[2], $settings);
    $output = array(
      '#theme' => 'collageformatter_collage_box',
      '#box' => $content,
      '#box_style' => implode(' ', $box_style),
    );
  }
  elseif ($box['box_type'] == 'image') {
    $image_uri = _collageformatter_image_file_check($box, $settings);
    $image_style = array(
      'display: block;',
      'max-width: 100%;',
      'height: auto;',
      'margin: 0;',
    );

    // TODO: use theme('image_formatter', ... ?
    $image = theme('image_style', array(
      'style_name' => 'collageformatter',
      'path' => $image_uri,
      'alt' => $box['alt'],
      'title' => $box['title'],
      'attributes' => array(
        'style' => implode(' ', $image_style),
      ),
    ));

    // Create image derivatives.
    if ($settings['generate_image_derivatives']) {
      $derivative_uri = image_style_path('collageformatter', $image_uri);
      if (!file_exists($derivative_uri)) {
        $image_style = image_style_load('collageformatter');
        if (!image_style_create_derivative($image_style, $image_uri, $derivative_uri)) {
          watchdog('collageformatter', 'Unable to generate the derived image located at %path.', array(
            '%path' => $derivative_uri,
          ));
        }
      }
    }
    $attached = array();

    // Process image linking and modal gallery settings.
    if ($settings['image_link'] == 'content') {
      $class = $settings['image_link_class'] ? array(
        $settings['image_link_class'],
      ) : array();
      $rel = $settings['image_link_rel'];
      $image = l($image, $box['content_uri'], array(
        'attributes' => array(
          'title' => $box['title'],
          'class' => $class,
          'rel' => $rel,
        ),
        'html' => TRUE,
      ));
    }
    elseif ($settings['image_link'] == 'file') {
      $image_dimensions = array(
        'width' => $box['width'],
        'height' => $box['height'],
      );
      if (empty($settings['image_link_image_style'])) {
        $image_url = file_create_url($box['uri']);
      }
      else {
        $image_url = image_style_url($settings['image_link_image_style'], $box['uri']);
        image_style_transform_dimensions($settings['image_link_image_style'], $image_dimensions);
      }
      $class = $settings['image_link_class'] ? array(
        $settings['image_link_class'],
      ) : array();
      $rel = $settings['image_link_rel'];
      $attributes = array();
      switch ($settings['image_link_modal']) {
        case 'colorbox':
          $class[] = 'colorbox';
          $rel = 'colorbox-' . $settings['gallery'];
          break;
        case 'shadowbox':
          $rel = 'shadowbox[' . $settings['gallery'] . ']';
          break;
        case 'fancybox':
          $class[] = 'fancybox';
          $attributes['data-fancybox-group'] = 'fancybox-' . $settings['gallery'];
          break;
        case 'photobox':
          $class[] = 'photobox';
          $attached = photobox_attached_resources();
          $attributes['data-photobox-gallery'] = 'photobox-' . $settings['gallery'];
          break;
        case 'photoswipe':
          $class[] = 'photoswipe';
          photoswipe_load_assets();
          $attributes['data-size'] = $image_dimensions['width'] . 'x' . $image_dimensions['height'];
          break;
        case 'lightbox2':
          $rel = 'lightbox[' . $settings['gallery'] . ']';
          break;
        default:
      }
      $image = l($image, $image_url, array(
        'attributes' => array(
          'title' => $box['title'],
          'class' => $class,
          'rel' => $rel,
        ) + $attributes,
        'html' => TRUE,
      ));
    }
    $image_wrapper_style = array(
      'float: left;',
      'max-width: ' . round($box['total_width'] - $settings['gap_size'] - 0.5) . 'px;',
      'width: ' . 100 * (round($box['total_width'] - $settings['gap_size'] - 0.5) / round($box['parent_total_width'] - 0.5)) . '%;',
    );
    if ($settings['gap_size']) {
      $margin_percentage = 100 * ($settings['gap_size'] / round($box['parent_total_width'] - 0.5));
      $image_wrapper_style[] = 'margin: ' . $margin_percentage . '% 0 0 ' . $margin_percentage . '%;';
    }
    $border = '';
    if ($settings['border_size']) {
      $border = 'border: ' . $settings['border_size'] . 'px solid';
      if ($settings['border_color']) {
        $border .= ' ' . $settings['border_color'];
      }
      $border .= ';';
    }
    $output = array(
      '#theme' => 'collageformatter_collage_image',
      '#image' => $image,
      '#image_wrapper_class' => array(
        'collageformatter-collage-image-wrapper-' . $box['delta'],
      ),
      '#image_wrapper_style' => implode(' ', $image_wrapper_style),
      '#image_style' => $border,
      '#attached' => $attached,
    );
  }
  return $output;
}

/**
 * Checks for/creates the original image reference file.
 */
function _collageformatter_image_file_check($box, $settings) {
  $image_width = round($box['total_width'] - 2 * $settings['border_size'] - $settings['gap_size'] - 0.5);
  $image_height = round($box['total_height'] - 2 * $settings['border_size'] - $settings['gap_size'] - 0.5);
  $filename = $image_width . 'x' . $image_height . '_' . $settings['advanced']['original_image_reference'] . '_' . drupal_basename($box['uri']);
  $directory = drupal_dirname(file_build_uri('collageformatter/' . file_uri_target($box['uri'])));
  $image_uri = $directory . '/' . $filename;
  if (!file_exists($image_uri)) {
    if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
      if ($settings['advanced']['original_image_reference'] == 'symlink') {
        if (!symlink(drupal_realpath($box['uri']), drupal_realpath($image_uri))) {
          watchdog('collageformatter', 'Failed to symlink file @source to @destination.', array(
            '@source' => $box['uri'],
            '@destination' => $image_uri,
          ));
        }
      }
      elseif ($settings['advanced']['original_image_reference'] == 'copy') {
        if (!file_unmanaged_copy($box['uri'], $image_uri, FILE_EXISTS_REPLACE)) {
          watchdog('collageformatter', 'Failed to copy file from @source to @destination.', array(
            '@source' => $box['uri'],
            '@destination' => $image_uri,
          ));
        }
      }
      elseif ($settings['advanced']['original_image_reference'] == 'fake') {
        $image = image_load($box['uri']);
        image_effect_apply($image, array(
          'effect callback' => 'image_scale_effect',
          'data' => array(
            'width' => 1,
            'height' => 1,
          ),
        ));
        image_save($image, $image_uri);
      }
    }
  }
  return $image_uri;
}

/**
 * Checks for upscaled images and returns the scaling factor.
 */
function _collageformatter_upscaling_check($box, $settings) {
  $scale1 = $scale2 = 1;
  if ($box['box_type'] == 'box') {
    $scale1 = _collageformatter_upscaling_check($box[1], $settings);
    $scale2 = _collageformatter_upscaling_check($box[2], $settings);
  }
  elseif ($box['box_type'] == 'image') {
    $width = $box['total_width'] - 2 * $settings['border_size'] - $settings['gap_size'];
    $height = $box['total_height'] - 2 * $settings['border_size'] - $settings['gap_size'];
    if ($box['width'] < $width) {
      $scale1 = $box['width'] / $width;
    }
    if ($box['height'] < $height) {
      $scale1 = $box['height'] / $height;
    }
  }
  return $scale1 <= $scale2 ? $scale1 : $scale2;
}

/**
 * Implements hook_theme().
 */
function collageformatter_theme($existing, $type, $theme, $path) {
  return array(
    'collageformatter_collage' => array(
      'variables' => array(
        'collage' => NULL,
        'collage_wrapper_class' => NULL,
        'collage_wrapper_style' => NULL,
        'collage_style' => NULL,
        'collage_bottom_style' => NULL,
      ),
      'template' => 'templates/collageformatter_collage',
    ),
    'collageformatter_collage_box' => array(
      'variables' => array(
        'box' => NULL,
        'box_style' => NULL,
      ),
      'template' => 'templates/collageformatter_collage_box',
    ),
    'collageformatter_collage_image' => array(
      'variables' => array(
        'image' => NULL,
        'image_wrapper_class' => NULL,
        'image_wrapper_style' => NULL,
        'image_style' => NULL,
      ),
      'template' => 'templates/collageformatter_collage_image',
    ),
  );
}

/**
 * Implements hook_image_default_styles().
 */
function collageformatter_image_default_styles() {
  $styles = array();
  $styles['collageformatter'] = array(
    'effects' => array(
      array(
        'name' => 'collageformatter',
        'weight' => '0',
      ),
    ),
  );
  return $styles;
}

/**
 * Implements hook_image_effect_info().
 */
function collageformatter_image_effect_info() {
  $effects = array();
  $effects['collageformatter'] = array(
    'label' => t('Collage Formatter'),
    'effect callback' => 'collageformatter_image_effect_callback',
    'dimensions passthrough' => TRUE,
  );
  return $effects;
}

/**
 * Collage Formatter image effect.
 */
function collageformatter_image_effect_callback(&$image, $data) {
  if (strpos(drupal_basename($image->source), '_copy_') !== FALSE || strpos(drupal_basename($image->source), '_symlink_') !== FALSE || strpos(drupal_basename($image->source), '_fake_') !== FALSE) {
    $dimensions = preg_replace('/.+\\/([\\d]+x[\\d]+)_(copy|symlink|fake)_.+/', '$1', $image->source);
    list($image_width, $image_height) = explode('x', $dimensions);
    $original_image_uri = preg_replace('/(.+\\/)collageformatter\\/(.+\\/)[\\d]+x[\\d]+_(copy|symlink|fake)_(.+)/', '$1$2$4', $image->source);

    // If it is a fake image - we need to load the real image resource.
    if (strpos(drupal_basename($image->source), '_fake_') !== FALSE) {
      $original_image = image_load($original_image_uri);
      $image->info = $original_image->info;
      $image->resource = $original_image->resource;
    }
    $image->source = $original_image_uri;
    $effect_callback = 'image_scale_and_crop_effect';
    if (module_exists('focal_point')) {
      $effect_callback = 'focal_point_scale_and_crop_effect';
    }
    if (isset($image_width) && isset($image_height)) {
      return image_effect_apply($image, array(
        'effect callback' => $effect_callback,
        'data' => array(
          'width' => $image_width,
          'height' => $image_height,
        ),
      ));
    }
  }
  return FALSE;
}

/**
 * Flushes collageformatter style images.
 */
function collageformatter_flush_style_submit() {
  $style = image_style_load('collageformatter');
  image_style_flush($style);
  drupal_set_message(t('Style %style has been flushed.', array(
    '%style' => 'collageformatter',
  )));
}

/**
 * Implements hook_image_style_flush().
 */
function collageformatter_image_style_flush($style) {

  // Remove symlinks and copied files.
  // Doesn't work for symlinks - deletes original file instead of symlink.
  if ($style['name'] == 'collageformatter') {
    $directory = file_build_uri('collageformatter');

    // file_unmanaged_delete_recursive($directory);
  }
}

/**
 * Implements hook_views_api().
 */
function collageformatter_views_api() {
  return array(
    'api' => 3,
  );
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function collageformatter_form_field_ui_display_overview_form_alter(&$form, &$form_state) {
  if (!empty($form_state['formatter_settings_edit'])) {
    $field = $form_state['formatter_settings_edit'];
    if ($form_state['input']['fields'][$field]['type'] == 'collageformatter') {
      $form['fields'][$field]['format']['settings_edit_form']['actions']['flush'] = array(
        '#type' => 'submit',
        '#value' => t('Flush generated images'),
        '#submit' => array(
          'collageformatter_flush_style_submit',
        ),
      );
    }
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function collageformatter_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'form_alter' && isset($implementations['collageformatter'])) {
    $group = $implementations['collageformatter'];
    unset($implementations['collageformatter']);
    $implementations['collageformatter'] = $group;
  }
}

Functions

Namesort descending Description
collageformatter_field_formatter_info Implements hook_field_formatter_info().
collageformatter_field_formatter_settings_form Implements hook_field_formatter_settings_form().
collageformatter_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
collageformatter_field_formatter_view Implements hook_field_formatter_view().
collageformatter_flush_style_submit Flushes collageformatter style images.
collageformatter_form_field_ui_display_overview_form_alter Implements hook_form_FORM_ID_alter().
collageformatter_image_default_styles Implements hook_image_default_styles().
collageformatter_image_effect_callback Collage Formatter image effect.
collageformatter_image_effect_info Implements hook_image_effect_info().
collageformatter_image_style_flush Implements hook_image_style_flush().
collageformatter_module_implements_alter Implements hook_module_implements_alter().
collageformatter_render_collage Returns renderable array of collages.
collageformatter_theme Implements hook_theme().
collageformatter_views_api Implements hook_views_api().
_collageformatter_default_settings Default Collage Formatter settings.
_collageformatter_image_file_check Checks for/creates the original image reference file.
_collageformatter_layout_box Recursive function to build the layout.
_collageformatter_render_box Recursive function to render the box.
_collageformatter_resize_box Recursive function to resize the box.
_collageformatter_scale_box Recursive function to scale the box using only one dimension.
_collageformatter_settings_form Settings form for Collage Formatter.
_collageformatter_upscaling_check Checks for upscaled images and returns the scaling factor.