| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 | /* 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;  var active = false;  var fontAttribute = "data-font-name";  function removeSelection() {    const divs = document.querySelectorAll(`span[${fontAttribute}]`);    for (const div of divs) {      div.className = "";    }  }  function resetSelection() {    const divs = document.querySelectorAll(`span[${fontAttribute}]`);    for (const div of divs) {      div.className = "debuggerHideText";    }  }  function selectFont(fontName, show) {    const divs = document.querySelectorAll(      `span[${fontAttribute}=${fontName}]`    );    for (const 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;      var tmp = document.createElement("button");      tmp.addEventListener("click", resetSelection);      tmp.textContent = "Refresh";      panel.appendChild(tmp);      fonts = document.createElement("div");      panel.appendChild(fonts);    },    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"]);      const 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 = URL.createObjectURL(          new Blob([fontObj.data], { type: 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);      });      const select = document.createElement("input");      select.setAttribute("type", "checkbox");      select.dataset.fontName = fontName;      select.addEventListener("click", function () {        selectFont(fontName, select.checked);      });      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;      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;  }  // eslint-disable-next-line no-shadow  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) {        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) {},    enabled: false,    active: false,    // Stats specific functions.    add(pageNumber, stat) {      if (!stat) {        return;      }      var statsIndex = getStatIndex(pageNumber);      if (statsIndex !== false) {        const 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");        }      }    },  };})();
 |