| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 | /* Copyright 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* eslint-disable no-var */'use strict';var FontInspector = (function FontInspectorClosure() {  var fonts, createObjectURL;  var active = false;  var fontAttribute = 'data-font-name';  function removeSelection() {    let divs = document.querySelectorAll(`span[${fontAttribute}]`);    for (let div of divs) {      div.className = '';    }  }  function resetSelection() {    let divs = document.querySelectorAll(`span[${fontAttribute}]`);    for (let div of divs) {      div.className = 'debuggerHideText';    }  }  function selectFont(fontName, show) {    let divs = document.querySelectorAll(`span[${fontAttribute}=${fontName}]`);    for (let div of divs) {      div.className = show ? 'debuggerShowText' : 'debuggerHideText';    }  }  function textLayerClick(e) {    if (!e.target.dataset.fontName ||        e.target.tagName.toUpperCase() !== 'SPAN') {      return;    }    var fontName = e.target.dataset.fontName;    var selects = document.getElementsByTagName('input');    for (var i = 0; i < selects.length; ++i) {      var select = selects[i];      if (select.dataset.fontName !== fontName) {        continue;      }      select.checked = !select.checked;      selectFont(fontName, select.checked);      select.scrollIntoView();    }  }  return {    // Properties/functions needed by PDFBug.    id: 'FontInspector',    name: 'Font Inspector',    panel: null,    manager: null,    init: function init(pdfjsLib) {      var panel = this.panel;      panel.setAttribute('style', 'padding: 5px;');      var tmp = document.createElement('button');      tmp.addEventListener('click', resetSelection);      tmp.textContent = 'Refresh';      panel.appendChild(tmp);      fonts = document.createElement('div');      panel.appendChild(fonts);      createObjectURL = pdfjsLib.createObjectURL;    },    cleanup: function cleanup() {      fonts.textContent = '';    },    enabled: false,    get active() {      return active;    },    set active(value) {      active = value;      if (active) {        document.body.addEventListener('click', textLayerClick, true);        resetSelection();      } else {        document.body.removeEventListener('click', textLayerClick, true);        removeSelection();      }    },    // FontInspector specific functions.    fontAdded: function fontAdded(fontObj, url) {      function properties(obj, list) {        var moreInfo = document.createElement('table');        for (var i = 0; i < list.length; i++) {          var tr = document.createElement('tr');          var td1 = document.createElement('td');          td1.textContent = list[i];          tr.appendChild(td1);          var td2 = document.createElement('td');          td2.textContent = obj[list[i]].toString();          tr.appendChild(td2);          moreInfo.appendChild(tr);        }        return moreInfo;      }      var moreInfo = properties(fontObj, ['name', 'type']);      var fontName = fontObj.loadedName;      var font = document.createElement('div');      var name = document.createElement('span');      name.textContent = fontName;      var download = document.createElement('a');      if (url) {        url = /url\(['"]?([^\)"']+)/.exec(url);        download.href = url[1];      } else if (fontObj.data) {        download.href = createObjectURL(fontObj.data, fontObj.mimeType);      }      download.textContent = 'Download';      var logIt = document.createElement('a');      logIt.href = '';      logIt.textContent = 'Log';      logIt.addEventListener('click', function(event) {        event.preventDefault();        console.log(fontObj);      });      var select = document.createElement('input');      select.setAttribute('type', 'checkbox');      select.dataset.fontName = fontName;      select.addEventListener('click', (function(select, fontName) {        return (function() {           selectFont(fontName, select.checked);        });      })(select, fontName));      font.appendChild(select);      font.appendChild(name);      font.appendChild(document.createTextNode(' '));      font.appendChild(download);      font.appendChild(document.createTextNode(' '));      font.appendChild(logIt);      font.appendChild(moreInfo);      fonts.appendChild(font);      // Somewhat of a hack, should probably add a hook for when the text layer      // is done rendering.      setTimeout(() => {        if (this.active) {          resetSelection();        }      }, 2000);    },  };})();var opMap;// Manages all the page steppers.var StepperManager = (function StepperManagerClosure() {  var steppers = [];  var stepperDiv = null;  var stepperControls = null;  var stepperChooser = null;  var breakPoints = Object.create(null);  return {    // Properties/functions needed by PDFBug.    id: 'Stepper',    name: 'Stepper',    panel: null,    manager: null,    init: function init(pdfjsLib) {      var self = this;      this.panel.setAttribute('style', 'padding: 5px;');      stepperControls = document.createElement('div');      stepperChooser = document.createElement('select');      stepperChooser.addEventListener('change', function(event) {        self.selectStepper(this.value);      });      stepperControls.appendChild(stepperChooser);      stepperDiv = document.createElement('div');      this.panel.appendChild(stepperControls);      this.panel.appendChild(stepperDiv);      if (sessionStorage.getItem('pdfjsBreakPoints')) {        breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));      }      opMap = Object.create(null);      for (var key in pdfjsLib.OPS) {        opMap[pdfjsLib.OPS[key]] = key;      }    },    cleanup: function cleanup() {      stepperChooser.textContent = '';      stepperDiv.textContent = '';      steppers = [];    },    enabled: false,    active: false,    // Stepper specific functions.    create: function create(pageIndex) {      var debug = document.createElement('div');      debug.id = 'stepper' + pageIndex;      debug.setAttribute('hidden', true);      debug.className = 'stepper';      stepperDiv.appendChild(debug);      var b = document.createElement('option');      b.textContent = 'Page ' + (pageIndex + 1);      b.value = pageIndex;      stepperChooser.appendChild(b);      var initBreakPoints = breakPoints[pageIndex] || [];      var stepper = new Stepper(debug, pageIndex, initBreakPoints);      steppers.push(stepper);      if (steppers.length === 1) {        this.selectStepper(pageIndex, false);      }      return stepper;    },    selectStepper: function selectStepper(pageIndex, selectPanel) {      var i;      pageIndex = pageIndex | 0;      if (selectPanel) {        this.manager.selectPanel(this);      }      for (i = 0; i < steppers.length; ++i) {        var stepper = steppers[i];        if (stepper.pageIndex === pageIndex) {          stepper.panel.removeAttribute('hidden');        } else {          stepper.panel.setAttribute('hidden', true);        }      }      var options = stepperChooser.options;      for (i = 0; i < options.length; ++i) {        var option = options[i];        option.selected = (option.value | 0) === pageIndex;      }    },    saveBreakPoints: function saveBreakPoints(pageIndex, bps) {      breakPoints[pageIndex] = bps;      sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));    },  };})();// The stepper for each page's IRQueue.var Stepper = (function StepperClosure() {  // Shorter way to create element and optionally set textContent.  function c(tag, textContent) {    var d = document.createElement(tag);    if (textContent) {      d.textContent = textContent;    }    return d;  }  function simplifyArgs(args) {    if (typeof args === 'string') {      var MAX_STRING_LENGTH = 75;      return args.length <= MAX_STRING_LENGTH ? args :        args.substring(0, MAX_STRING_LENGTH) + '...';    }    if (typeof args !== 'object' || args === null) {      return args;    }    if ('length' in args) { // array      var simpleArgs = [], i, ii;      var MAX_ITEMS = 10;      for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {        simpleArgs.push(simplifyArgs(args[i]));      }      if (i < args.length) {        simpleArgs.push('...');      }      return simpleArgs;    }    var simpleObj = {};    for (var key in args) {      simpleObj[key] = simplifyArgs(args[key]);    }    return simpleObj;  }  function Stepper(panel, pageIndex, initialBreakPoints) {    this.panel = panel;    this.breakPoint = 0;    this.nextBreakPoint = null;    this.pageIndex = pageIndex;    this.breakPoints = initialBreakPoints;    this.currentIdx = -1;    this.operatorListIdx = 0;  }  Stepper.prototype = {    init: function init(operatorList) {      var panel = this.panel;      var content = c('div', 'c=continue, s=step');      var table = c('table');      content.appendChild(table);      table.cellSpacing = 0;      var headerRow = c('tr');      table.appendChild(headerRow);      headerRow.appendChild(c('th', 'Break'));      headerRow.appendChild(c('th', 'Idx'));      headerRow.appendChild(c('th', 'fn'));      headerRow.appendChild(c('th', 'args'));      panel.appendChild(content);      this.table = table;      this.updateOperatorList(operatorList);    },    updateOperatorList: function updateOperatorList(operatorList) {      var self = this;      function cboxOnClick() {        var x = +this.dataset.idx;        if (this.checked) {          self.breakPoints.push(x);        } else {          self.breakPoints.splice(self.breakPoints.indexOf(x), 1);        }        StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);      }      var MAX_OPERATORS_COUNT = 15000;      if (this.operatorListIdx > MAX_OPERATORS_COUNT) {        return;      }      var chunk = document.createDocumentFragment();      var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,                                        operatorList.fnArray.length);      for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {        var line = c('tr');        line.className = 'line';        line.dataset.idx = i;        chunk.appendChild(line);        var checked = this.breakPoints.includes(i);        var args = operatorList.argsArray[i] || [];        var breakCell = c('td');        var cbox = c('input');        cbox.type = 'checkbox';        cbox.className = 'points';        cbox.checked = checked;        cbox.dataset.idx = i;        cbox.onclick = cboxOnClick;        breakCell.appendChild(cbox);        line.appendChild(breakCell);        line.appendChild(c('td', i.toString()));        var fn = opMap[operatorList.fnArray[i]];        var decArgs = args;        if (fn === 'showText') {          var glyphs = args[0];          var newArgs = [];          var str = [];          for (var j = 0; j < glyphs.length; j++) {            var glyph = glyphs[j];            if (typeof glyph === 'object' && glyph !== null) {              str.push(glyph.fontChar);            } else {              if (str.length > 0) {                newArgs.push(str.join(''));                str = [];              }              newArgs.push(glyph); // null or number            }          }          if (str.length > 0) {            newArgs.push(str.join(''));          }          decArgs = [newArgs];        }        line.appendChild(c('td', fn));        line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs))));      }      if (operatorsToDisplay < operatorList.fnArray.length) {        line = c('tr');        var lastCell = c('td', '...');        lastCell.colspan = 4;        chunk.appendChild(lastCell);      }      this.operatorListIdx = operatorList.fnArray.length;      this.table.appendChild(chunk);    },    getNextBreakPoint: function getNextBreakPoint() {      this.breakPoints.sort(function(a, b) {        return a - b;      });      for (var i = 0; i < this.breakPoints.length; i++) {        if (this.breakPoints[i] > this.currentIdx) {          return this.breakPoints[i];        }      }      return null;    },    breakIt: function breakIt(idx, callback) {      StepperManager.selectStepper(this.pageIndex, true);      var self = this;      var dom = document;      self.currentIdx = idx;      var listener = function(e) {        switch (e.keyCode) {          case 83: // step            dom.removeEventListener('keydown', listener);            self.nextBreakPoint = self.currentIdx + 1;            self.goTo(-1);            callback();            break;          case 67: // continue            dom.removeEventListener('keydown', listener);            var breakPoint = self.getNextBreakPoint();            self.nextBreakPoint = breakPoint;            self.goTo(-1);            callback();            break;        }      };      dom.addEventListener('keydown', listener);      self.goTo(idx);    },    goTo: function goTo(idx) {      var allRows = this.panel.getElementsByClassName('line');      for (var x = 0, xx = allRows.length; x < xx; ++x) {        var row = allRows[x];        if ((row.dataset.idx | 0) === idx) {          row.style.backgroundColor = 'rgb(251,250,207)';          row.scrollIntoView();        } else {          row.style.backgroundColor = null;        }      }    },  };  return Stepper;})();var Stats = (function Stats() {  var stats = [];  function clear(node) {    while (node.hasChildNodes()) {      node.removeChild(node.lastChild);    }  }  function getStatIndex(pageNumber) {    for (var i = 0, ii = stats.length; i < ii; ++i) {      if (stats[i].pageNumber === pageNumber) {        return i;      }    }    return false;  }  return {    // Properties/functions needed by PDFBug.    id: 'Stats',    name: 'Stats',    panel: null,    manager: null,    init(pdfjsLib) {      this.panel.setAttribute('style', 'padding: 5px;');    },    enabled: false,    active: false,    // Stats specific functions.    add(pageNumber, stat) {      if (!stat) {        return;      }      var statsIndex = getStatIndex(pageNumber);      if (statsIndex !== false) {        var b = stats[statsIndex];        this.panel.removeChild(b.div);        stats.splice(statsIndex, 1);      }      var wrapper = document.createElement('div');      wrapper.className = 'stats';      var title = document.createElement('div');      title.className = 'title';      title.textContent = 'Page: ' + pageNumber;      var statsDiv = document.createElement('div');      statsDiv.textContent = stat.toString();      wrapper.appendChild(title);      wrapper.appendChild(statsDiv);      stats.push({ pageNumber, div: wrapper, });      stats.sort(function(a, b) {        return a.pageNumber - b.pageNumber;      });      clear(this.panel);      for (var i = 0, ii = stats.length; i < ii; ++i) {        this.panel.appendChild(stats[i].div);      }    },    cleanup() {      stats = [];      clear(this.panel);    },  };})();// Manages all the debugging tools.window.PDFBug = (function PDFBugClosure() {  var panelWidth = 300;  var buttons = [];  var activePanel = null;  return {    tools: [      FontInspector,      StepperManager,      Stats    ],    enable(ids) {      var all = false, tools = this.tools;      if (ids.length === 1 && ids[0] === 'all') {        all = true;      }      for (var i = 0; i < tools.length; ++i) {        var tool = tools[i];        if (all || ids.includes(tool.id)) {          tool.enabled = true;        }      }      if (!all) {        // Sort the tools by the order they are enabled.        tools.sort(function(a, b) {          var indexA = ids.indexOf(a.id);          indexA = indexA < 0 ? tools.length : indexA;          var indexB = ids.indexOf(b.id);          indexB = indexB < 0 ? tools.length : indexB;          return indexA - indexB;        });      }    },    init(pdfjsLib, container) {      /*       * Basic Layout:       * PDFBug       *  Controls       *  Panels       *    Panel       *    Panel       *    ...       */      var ui = document.createElement('div');      ui.id = 'PDFBug';      var controls = document.createElement('div');      controls.setAttribute('class', 'controls');      ui.appendChild(controls);      var panels = document.createElement('div');      panels.setAttribute('class', 'panels');      ui.appendChild(panels);      container.appendChild(ui);      container.style.right = panelWidth + 'px';      // Initialize all the debugging tools.      var tools = this.tools;      var self = this;      for (var i = 0; i < tools.length; ++i) {        var tool = tools[i];        var panel = document.createElement('div');        var panelButton = document.createElement('button');        panelButton.textContent = tool.name;        panelButton.addEventListener('click', (function(selected) {          return function(event) {            event.preventDefault();            self.selectPanel(selected);          };        })(i));        controls.appendChild(panelButton);        panels.appendChild(panel);        tool.panel = panel;        tool.manager = this;        if (tool.enabled) {          tool.init(pdfjsLib);        } else {          panel.textContent = tool.name + ' is disabled. To enable add ' +                              ' "' + tool.id + '" to the pdfBug parameter ' +                              'and refresh (separate multiple by commas).';        }        buttons.push(panelButton);      }      this.selectPanel(0);    },    cleanup() {      for (var i = 0, ii = this.tools.length; i < ii; i++) {        if (this.tools[i].enabled) {          this.tools[i].cleanup();        }      }    },    selectPanel(index) {      if (typeof index !== 'number') {        index = this.tools.indexOf(index);      }      if (index === activePanel) {        return;      }      activePanel = index;      var tools = this.tools;      for (var j = 0; j < tools.length; ++j) {        if (j === index) {          buttons[j].setAttribute('class', 'active');          tools[j].active = true;          tools[j].panel.removeAttribute('hidden');        } else {          buttons[j].setAttribute('class', '');          tools[j].active = false;          tools[j].panel.setAttribute('hidden', 'true');        }      }    },  };})();
 |