Tutorial

Google Viewer PDF embed loading only occasionally? FIX

Every modern browser supports displaying PDF in it's own way. But sometimes you stumble upon a client, which does not like, that every person has different PDF viewer.

Now the problem is, that there are no PDF viewers out there (I mean, JS libraries) and under development, since there is no need to have some. And most of them just works the way they check whether your browser supports the displaying of directly embedded PDF's or not, and THEN it inserts it's own PDF viewer. Which is unreliable.

So we needed to have a solution, when every person, regardless of browser (and even explorer compatible) has the same PDF viewer so the result is predictible.

This could be easily achieved by Google's Embedded Media Viewer (which btw supports not only PDFs, but also other media as well), but the problem is, Google also has some kind of firewall of some sort which just kicks the requests from time to time. So the displaying of PDF's became unreliable. Essentially it worked like 50 percent of time. Sometimes it would load the PDF, and sometimes I would be staring at a blank page and nothing was loaded.

At first I was thinking that this is some kind of AdBlock issue, that only if I have adblock I got blocked by google's policies or something, but disabling AdBlock did not helped at all.

Solution for Drupal, but not only for Drupal.

You can use this one for your system as well, but you will need to adjust the code accordingly.

Basicly what it does is that it tries to load the iframe contents indefinitely, until they are finally loaded. And to prevent people from staring at a blank page forever there is also a message saying that PDF is being loaded.

 

Pridal/a lubo dňa Št, 01/28/2021 - 14:17
Make a field for uploading PDFs

In Drupal, just create a field on your content type of type "file". And allow only PDF's to be uploaded.

Manage Display settings

In Manage Display settings, set it to have "Generic URL address" as a formatter. This way it will print only URL address and we can use it later.

Make a field override in your theme

Navigate to /sites/all/themes/[YOUR THEME]/templates folder and copy file field.tpl.php which you can find in /modules/field directory directly in ROOT folder of your website.

Now rename the file accordingly, check machine name of your field and rename it like following:

field--[type|name[--content-type]|content-type].tpl.php

base template: field.tpl.php

Theme hook suggestions are made based on these factors, listed from the most specific template to the least. Drupal will use the most specific template it finds:

  1. field--field-name--content-type.tpl.php
  2. field--content-type.tpl.php
  3. field--field-name.tpl.php
  4. field--field-type.tpl.php

Note that underscores in a Field's machine name are replaced by hyphens. Also remember to include "field-" in custom field names, e.g: field--field-phone.tpl.php.

See field.tpl.php in the Drupal API documentation for more information.

Inside, place the following code
<?php

/**
 * @file field.tpl.php
 * Default template implementation to display the value of a field.
 *
 * This file is not used by Drupal core, which uses theme functions instead for
 * performance reasons. The markup is the same, though, so if you want to use
 * template files rather than functions to extend field theming, copy this to
 * your custom theme. See theme_field() for a discussion of performance.
 *
 * Available variables:
 * - $items: An array of field values. Use render() to output them.
 * - $label: The item label.
 * - $label_hidden: Whether the label display is set to 'hidden'.
 * - $classes: String of classes that can be used to style contextually through
 *   CSS. It can be manipulated through the variable $classes_array from
 *   preprocess functions. The default values can be one or more of the
 *   following:
 *   - field: The current template type, i.e., "theming hook".
 *   - field-name-[field_name]: The current field name. For example, if the
 *     field name is "field_description" it would result in
 *     "field-name-field-description".
 *   - field-type-[field_type]: The current field type. For example, if the
 *     field type is "text" it would result in "field-type-text".
 *   - field-label-[label_display]: The current label position. For example, if
 *     the label position is "above" it would result in "field-label-above".
 *
 * Other variables:
 * - $element['#object']: The entity to which the field is attached.
 * - $element['#view_mode']: View mode, e.g. 'full', 'teaser'...
 * - $element['#field_name']: The field name.
 * - $element['#field_type']: The field type.
 * - $element['#field_language']: The field language.
 * - $element['#field_translatable']: Whether the field is translatable or not.
 * - $element['#label_display']: Position of label display, inline, above, or
 *   hidden.
 * - $field_name_css: The css-compatible field name.
 * - $field_type_css: The css-compatible field type.
 * - $classes_array: Array of html class attribute values. It is flattened
 *   into a string within the variable $classes.
 *
 * @see template_preprocess_field()
 * @see theme_field()
 *
 * @ingroup themeable
 */
?>
<!--
This file is not used by Drupal core, which uses theme functions instead.
See http://api.drupal.org/api/function/theme_field/7 for details.
After copying this file to your theme's folder and customizing it, remove this
HTML comment.
-->
<div class="<?php print $classes; ?>"<?php print $attributes; ?>>
  <?php if (!$label_hidden): ?>
    <div class="field-label"<?php print $title_attributes; ?>><?php print $label ?>:&nbsp;</div>
  <?php endif; ?>
  <div class="field-items"<?php print $content_attributes; ?>>
    <?php foreach ($items as $delta => $item): ?>
    <?php 
      /* WE REMOVED THAT PART
      <div class="field-item <?php print $delta % 2 ? 'odd' : 'even'; ?>"<?php print $item_attributes[$delta]; ?>><?php print render($item); ?></div>
      */
    ?>
      <div id="test-id-1" style="text-align: center; width: 100%; height: 1150px" class="embed-pdf" data-url="<?php print render($item); ?>"><span class="loader">Please wait, PDF is loading...</span></div>

    <?php endforeach; ?>
  </div>
</div>
Edit your Javascript file in theme folder

So now we only need to add the following snipplet in your javascript file in your theme folder.

Note, that it uses jquery so install jquery update module beforehand.

/* PDF FIX */
$(document).ready(function() {

    let embed_pdfs = {};

    $('.embed-pdf').each(function() {
        var $pdfViewer = $('<iframe src="https://docs.google.com/viewer?url=' + $(this).data('url') + '&embedded=true" style="width: 100%; height: 100%" frameborder="0" scrolling="no"></iframe>');
        $pdfViewer.appendTo($(this));
        console.log($(this).attr('id') + " created");
        embed_pdfs[$(this).attr('id')] = 'created';
    });

    $(document).find('.embed-pdf iframe').load(function(){
        embed_pdfs[$(this).parents('.embed-pdf').attr('id')] = 'loaded';
        $(this).siblings('.loader').remove();
        console.log($(this).parents('.embed-pdf').attr('id') + " loaded");
    });

    let embed_pdf_check = setInterval(function() {
        let remaining_embeds = 0;
        $.each(embed_pdfs, function(key, value) {
            try {
                if ($($('#' + key)).find('iframe').contents().find("body").contents().length == 0) {
                    remaining_embeds++;
                    console.log(key + " resetting");
                    $($('#' + key)).find('iframe').attr('src', src='https://docs.google.com/viewer?url=' + $('#' + key).data('url') + '&embedded=true');
                }
            }
            catch(err) {
                console.log(key + " reloading");
            }
        });
    
        if (!remaining_embeds) {
            clearInterval(embed_pdf_check);
        }
    }, 1000);
});
Done!

Do not forger to clear the cache, so everything takes an effect.

Now it should be working just fine. In times where browser struggle to load the PDF, it will try each second again. I found it to be working perfectly.

Note that the solution is not mine, I just applied it on my use case, check this stackoverflow thread for original solution.

 

Might interest you

Tutorial
In the last months I am being pretty much bombarded by my clients with asking what happened to their sites. That the spam protection is not…
Tutorial
So there is a very good and user friendly module called Node Order, but it has it's flaws. Each time I use it, it creates duplicates based…

Recommended

Article
32 views
For the past few days I am trying to comprehend why / how this blockchain even gained it'…
Tutorial
95 views
This sketch is quite easy, I used Arduino Nano with OLED 0.96″ display 128×64 resolution…
Tutorial
157 views
While working on a fairly complex website with very complex views setup, including tens…
Tutorial
14 views
In this case we have two options, either we use hook_user_presave() or we can create new…
Tutorial
15 views
When using Swiftmailer under Drupal 8 / 9 it automatically sets the headers for sender to…
Tutorial
7 views
Yes, IOS / Safari is the new internet explorer. Amount of time I spend on debugging…
Tutorial
43 views
There is a very handy function in Drupal 8 / 9, allowing developers refresh view when…
Tutorial
22 views
Often, when doing SEO checkups, SEO specialist come up with adding Schema.org…
Tutorial
174 views
I needed to test my contracts against USDC contract, specifically I needed ERC-721 mint…
Tutorial
85 views
If you are a newbie like I am and struggling with setting the proper MYSQL my.cnf config…
Tutorial
25 views
I had trouble to set this up properly, because documentation is quite misleading or often…
Article
72 views
As the title says, DO NOT in any circumstances install ANY bitcoin price extension to ANY…
Tutorial
278 views
This is (or should be) a working example of sending some Ether between two addresses.…
Module
45 views
This list was fetched from Zapper, with their /v1/token-list endpoint. Which you can…
Tutorial
143 views
In the last months I am being pretty much bombarded by my clients with asking what…