0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PDF分割の指定を検査する:抜けと重複を返し、順序は別に確かめる

0
Posted at

PDFを複数のファイルに分割する前に、指定したパートが元の資料の全ページを一度ずつ含んでいるかを確かめたいことがあります。4ページの資料を2つに分ける指定 [[0,1],[1,3]] は、要素数こそ4ですが、1 が重複し、2 が欠けています。画面上の番号でいえば、この指定のまま分割すると2ページ目が両方のファイルに入り、3ページ目はどちらにも入りません。

もう一つ、逆順の例があります。0始まりのインデックスで [[3,2],[1,0]] と指定すると、0〜3はすべて一度ずつ現れます。しかし、資料を先頭から順番に渡したい場合、この並びは期待と逆です。「全ページを網羅できた」と「順番が正しい」は、別々に確認する必要があります。

まず網羅だけを検査する

次の関数は、各ページのインデックスがちょうど一度ずつ現れるかを調べます。欠けた番号を missing、繰り返された番号を duplicates に返し、両方が空なら complete を true にします。返す番号は画面上のページ番号ではなく、0始まりのインデックスです。利用者に見せるときは、表示用の番号に直してから伝えると誤解が減ります。

逆順の例は、この関数の契約では complete: true です。順番まで正しいという意味ではありません。連続した範囲を元の順序で渡したいなら、その要件に合わせた別の検査が必要です。

入力として不正なものは網羅不足として扱わず、例外にします。空のパート、負の値、範囲外の値、小数、文字列などが該当します。教材上の上限はページ数1〜1,000、パート数1〜100、出現回数の合計2,000です。入力配列は変更しません。

export function partitionCoverage(parts, pageCount) {
  if (!Number.isSafeInteger(pageCount) || pageCount < 1 || pageCount > 1000 ||
      !Array.isArray(parts) || parts.length < 1 || parts.length > 100) {
    throw new TypeError('invalid partition input');
  }
  const seen = new Set(), repeated = new Set();
  let occurrences = 0;
  for (const part of parts) {
    if (!Array.isArray(part) || !part.length || (occurrences += part.length) > 2000) {
      throw new TypeError('invalid part');
    }
    for (const page of part) {
      if (!Number.isSafeInteger(page) || page < 0 || page >= pageCount) {
        throw new TypeError('invalid zero-based page index');
      }
      if (seen.has(page)) repeated.add(page);
      seen.add(page);
    }
  }
  const missing = Array.from({length: pageCount}, (_, i) => i).filter(i => !seen.has(i));
  const duplicates = [...repeated].sort((a, b) => a - b);
  return {complete: missing.length === 0 && duplicates.length === 0, missing, duplicates};
}

テストで逆順も残す

以下を関数の後ろに置き、同じ .mjs ファイルにまとめます。valid は「検査できる入力」という意味で、網羅が不完全な例も含みます。逆順の例が true になることをテストに明記しておくと、後からこの関数を順序の検査と取り違えにくくなります。入力が変更されていないことも確認します。

import assert from 'node:assert/strict';
const valid = [
 [[[0,1],[2,3]],4,{complete:true,missing:[],duplicates:[]}],
 [[[0,1],[1,3]],4,{complete:false,missing:[2],duplicates:[1]}],
 [[[3,2],[1,0]],4,{complete:true,missing:[],duplicates:[]}],
 [[[0,0]],1,{complete:false,missing:[],duplicates:[0]}],
 [[[0]],3,{complete:false,missing:[1,2],duplicates:[]}]
];
for (const [parts,count,expected] of valid) {
 const before=JSON.stringify(parts);assert.deepEqual(partitionCoverage(parts,count),expected);
 assert.equal(JSON.stringify(parts),before);
}
const invalid = [[[],3],[[[]],3],[[[3]],3],[[[-1]],3],[[['1']],3],[[[0.5]],3],[[[0]],0],[[[0]],1001],[[Array(2001).fill(0)],1],[Array(101).fill([0]),1]];
for(const [parts,count] of invalid) assert.throws(()=>partitionCoverage(parts,count));
console.log(JSON.stringify({valid:valid.length,rejected:invalid.length,boundary:'coverage only, not order or real PDF output'}));

11月1日の保存済み Node v22.23.4 実行記録では、検査可能な入力5件と拒否入力10件を扱い、スタブは終了コード1、実装は0でした。この結果は、ページの実体、印字番号、表示やナビゲーションまでは確かめていません。PDFのデコード・書き込み、ZIP作成、ブラウザーや端末での動作、変換もテストしていません。

開示:GPT-6.1 Terra を使って Yan Wang / LensUp のために執筆しました。Yan Wang は LensUp の関係者です。本文は独立した編集AI(lensup 主编)が推敲し、Codex(AI)が事実確認と、記録済みの合成入力によるコード実行結果との照合を行いました。人間の編集者やオーナーによる確認・テストは行っていません。根拠は11月1日のソースと公開ページの確認、および上記の保存済み合成Node実行記録です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?