pdfh5.js 147 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743
  1. ; (function (g, fn) {
  2. var version = "3.0.0",
  3. pdfjsVersion = "5.4.296";
  4. console.info("pdfh5.js v" + version + " && pdf.js v" + pdfjsVersion + " https://pdfh5.gjtool.cn");
  5. if (!g.document) {
  6. throw new Error("pdfh5 requires a window with a document");
  7. }
  8. async function initPdfJs() {
  9. if (g.pdfjsLib) {
  10. return g.pdfjsLib;
  11. }
  12. try {
  13. let pdfjsLib = await import('./pdf.min.js');
  14. let sandboxModule = await import('./pdf.sandbox.min.js');
  15. let workerSrc = './js/pdf.worker.min.js';
  16. let cMapUrl = '../cmaps/';
  17. let standardFontDataUrl = '../standard_fonts/';
  18. let iccUrl = '../iccs/';
  19. let wasmUrl = '../wasm/';
  20. // 集成沙箱功能
  21. if (sandboxModule && sandboxModule.default) {
  22. pdfjsLib.Sandbox = sandboxModule.default;
  23. pdfjsLib.SandboxManager = sandboxModule.SandboxManager;
  24. }
  25. pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;
  26. var resourcePaths = {
  27. workerSrc: workerSrc,
  28. cMapUrl: cMapUrl,
  29. standardFontDataUrl: standardFontDataUrl,
  30. iccUrl: iccUrl,
  31. wasmUrl: wasmUrl
  32. };
  33. window._pdfh5ResourcePaths = resourcePaths;
  34. return pdfjsLib;
  35. } catch (error) {
  36. console.error('Failed to load PDF.js:', error);
  37. throw error;
  38. }
  39. }
  40. // 创建Pdfh5构造函数
  41. var Pdfh5Constructor = fn(version, initPdfJs);
  42. if (typeof define === 'function' && define.amd) {
  43. define(function () {
  44. return Pdfh5Constructor;
  45. });
  46. } else if (typeof module !== 'undefined' && module.exports) {
  47. module.exports = Pdfh5Constructor;
  48. } else {
  49. g.Pdfh5 = Pdfh5Constructor;
  50. }
  51. })(typeof window !== 'undefined' ? window : this, function (version, initPdfJs) {
  52. 'use strict';
  53. var css = '.pdfjs {width: 100%;height: 100%;overflow: hidden;background: #fff;position: relative;}.pdfjs .viewerContainer {position: relative;width: 100%;height: 100%;overflow-y: auto;overflow-x: hidden;-webkit-overflow-scrolling: touch;transition: all .3s;}.pdfjs .pdfViewer {position: relative;top: 0;left: 0;padding: 10px 8px;}.pdfjs .pdfViewer .pageContainer {margin: 0px auto 8px auto;position: relative;overflow: visible;-webkit-box-shadow: darkgrey 0px 1px 3px 0px;-moz-box-shadow: darkgrey 0px 1px 3px 0px;box-shadow: darkgrey 0px 1px 3px 0px;background-color: white;box-sizing: border-box;}.pdfjs .pdfViewer .pageContainer img {width: 100%;height: 100%;position: relative;z-index: 100;user-select: none;pointer-events: none;}.pdfjs .pdfViewer .pageContainer canvas {width: 100%;height: 100%;position: relative;z-index: 100;user-select: none;pointer-events: none;}.pdfjs .pageNum {padding: 0px 7px;height: 26px;position: absolute;top: 20px;left: 15px;z-index: 997;border-radius: 8px;transition: all .3s;display: none;}.pdfjs .pageNum-bg, .pdfjs .pageNum-num {width: 100%;height: 100%;line-height: 26px;text-align: center;position: absolute;top: 0px;left: 0px;color: #fff;border-radius: 8px;font-size: 16px;}.pdfjs .pageNum-bg {background: rgba(0, 0, 0, 0.5);}.pdfjs .pageNum-num {position: relative;}.pdfjs .pageNum span {color: #fff;font-size: 16px;}.pdfjs .loadingBar {position: absolute;width: 100%;z-index: 1000;background: #fff !important;height: 4px;top: 0px;left: 0px;transition: all .3s;}.pdfjs .loadingBar .progress {background: #fff !important;position: absolute;top: 0;left: 0;width: 0%;height: 100%;overflow: hidden;transition: width 200ms;}.pdfjs .loadingBar .progress .glimmer {position: absolute;top: 0;left: 0;height: 100%;width: calc(100% + 150px);background: #7bcf34;}.pdfjs .backTop {width: 50px;height: 50px;line-height: 50px;text-align: center;position: absolute;bottom: 90px;right: 15px;font-size: 18px;z-index: 999;border-radius: 50%;background: rgba(0, 0, 0, 0.4) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAA+klEQVRYw+2WUQ2DMBCG2TIBSJiESkACEpCAg83BcLBJmIQ5gClgDpiDby9tciGkoaUtZOESXuhdv7+X/pdm2dYC6IgX7Zh3THy+w9oN/rMASqBcE26iSA1XwCAEDIBKBc8F/KE/gB7IU8BbDXyJf2Z2tFFFAE8N6iRIi/jotXssuGn1FzhPrCu9BtCEhlcCrix5hbiYVSh46bKpELvcniO71Q51zWJ7ju3mUe9vzym7eR7Az57CbohTXBzAt9GknG9PoLY8KK4z6htLfeXTTXMZAfoZuWYWKC+YZWMAQuWZSP0k2wXsAnYB2xNwci1wGTKhO/COlLtu/ABVfTFsxwwYRgAAAABJRU5ErkJggg==) no-repeat center;background-size: 50% 50%;transition: all .3s;display: none;}.pdfjs .loadEffect {width: 100px;height: 100px;position: absolute;top: 50%;left: 50%;margin-top: -50px;margin-left: -50px;z-index: 103;background: url(data:image/gif;base64,R0lGODlhgACAAKIAAP///93d3bu7u5mZmQAA/wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwCAAIAfAB8AAAD/0i63P4wygYqmDjrzbtflvWNZGliYXiubKuloivPLlzReD7al+7/Eh5wSFQIi8hHYBkwHUmD6CD5YTJLz49USuVYraRsZ7vtar7XnQ1Kjpoz6LRHvGlz35O4nEPP2O94EnpNc2sef1OBGIOFMId/inB6jSmPdpGScR19EoiYmZobnBCIiZ95k6KGGp6ni4wvqxilrqBfqo6skLW2YBmjDa28r6Eosp27w8Rov8ekycqoqUHODrTRvXsQwArC2NLF29UM19/LtxO5yJd4Au4CK7DUNxPebG4e7+8n8iv2WmQ66BtoYpo/dvfacBjIkITBE9DGlMvAsOIIZjIUAixliv9ixYZVtLUos5GjwI8gzc3iCGghypQqrbFsme8lwZgLZtIcYfNmTJ34WPTUZw5oRxdD9w0z6iOpO15MgTh1BTTJUKos39jE+o/KS64IFVmsFfYT0aU7capdy7at27dw48qdS7eu3bt480I02vUbX2F/JxYNDImw4GiGE/P9qbhxVpWOI/eFKtlNZbWXuzlmG1mv58+gQ4seTbq06dOoU6vGQZJy0FNlMcV+czhQ7SQmYd8eMhPs5BxVdfcGEtV3buDBXQ+fURxx8oM6MT9P+Fh6dOrH2zavc13u9JXVJb520Vp8dvC76wXMuN5Sepm/1WtkEZHDefnzR9Qvsd9+/wi8+en3X0ntYVcSdAE+UN4zs7ln24CaLagghIxBaGF8kFGoIYV+Ybghh841GIyI5ICIFoklJsigihmimJOLEbLYIYwxSgigiZ+8l2KB+Ml4oo/w8dijjcrouCORKwIpnJIjMnkkksalNeR4fuBIm5UEYImhIlsGCeWNNJphpJdSTlkml1jWeOY6TnaRpppUctcmFW9mGSaZceYopH9zkjnjUe59iR5pdapWaGqHopboaYualqije67GJ6CuJAAAIfkEBQUABAAsCgACAFcAMAAAA/9Iutz+ML5Ag7w46z0r5WAoSp43nihXVmnrdusrv+s332dt4Tyo9yOBUJD6oQBIQGs4RBlHySSKyczVTtHoidocPUNZaZAr9F5FYbGI3PWdQWn1mi36buLKFJvojsHjLnshdhl4L4IqbxqGh4gahBJ4eY1kiX6LgDN7fBmQEJI4jhieD4yhdJ2KkZk8oiSqEaatqBekDLKztBG2CqBACq4wJRi4PZu1sA2+v8C6EJexrBAD1AOBzsLE0g/V1UvYR9sN3eR6lTLi4+TlY1wz6Qzr8u1t6FkY8vNzZTxaGfn6mAkEGFDgL4LrDDJDyE4hEIbdHB6ESE1iD4oVLfLAqPETIsOODwmCDJlv5MSGJklaS6khAQAh+QQFBQAEACwfAAIAVwAwAAAD/0i63A4QuEmrvTi3yLX/4MeNUmieITmibEuppCu3sDrfYG3jPKbHveDktxIaF8TOcZmMLI9NyBPanFKJp4A2IBx4B5lkdqvtfb8+HYpMxp3Pl1qLvXW/vWkli16/3dFxTi58ZRcChwIYf3hWBIRchoiHiotWj5AVkpIXi4xLjxiaiJR/T5ehoomcnZ+EGamqq6VGoK+pGqxCtaiiuJVBu7yaHrk4pxqwxMUzwcKbyrPMzZG90NGDrh/JH8t72dq3IN1jfCHb3L/e5ebh4ukmxyDn6O8g08jt7tf26ybz+m/W9GNXzUQ9fm1Q/APoSWAhhfkMAmpEbRhFKwsvCsmosRIHx444PoKcIXKkjIImjTzjkQAAIfkEBQUABAAsPAA8AEIAQgAAA/VIBNz+8KlJq72Yxs1d/uDVjVxogmQqnaylvkArT7A63/V47/m2/8CgcEgsGo/IpHLJbDqf0Kh0Sj0FroGqDMvVmrjgrDcTBo8v5fCZki6vCW33Oq4+0832O/at3+f7fICBdzsChgJGeoWHhkV0P4yMRG1BkYeOeECWl5hXQ5uNIAOjA1KgiKKko1CnqBmqqk+nIbCkTq20taVNs7m1vKAnurtLvb6wTMbHsUq4wrrFwSzDzcrLtknW16tI2tvERt6pv0fi48jh5h/U6Zs77EXSN/BE8jP09ZFA+PmhP/xvJgAMSGBgQINvEK5ReIZhQ3QEMTBLAAAh+QQFBQAEACwCAB8AMABXAAAD50i6DA4syklre87qTbHn4OaNYSmNqKmiqVqyrcvBsazRpH3jmC7yD98OCBF2iEXjBKmsAJsWHDQKmw571l8my+16v+CweEwum8+hgHrNbrvbtrd8znbR73MVfg838f8BeoB7doN0cYZvaIuMjY6PkJGSk2gClgJml5pjmp2YYJ6dX6GeXaShWaeoVqqlU62ir7CXqbOWrLafsrNctjIDwAMWvC7BwRWtNsbGFKc+y8fNsTrQ0dK3QtXAYtrCYd3eYN3c49/a5NVj5eLn5u3s6e7x8NDo9fbL+Mzy9/T5+tvUzdN3Zp+GBAAh+QQJBQAEACwCAAIAfAB8AAAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdArcQK2TOL7/nl4PSMwIfcUk5YhUOh3M5nNKiOaoWCuWqt1Ou16l9RpOgsvEMdocXbOZ7nQ7DjzTaeq7zq6P5fszfIASAYUBIYKDDoaGIImKC4ySH3OQEJKYHZWWi5iZG0ecEZ6eHEOio6SfqCaqpaytrpOwJLKztCO2jLi1uoW8Ir6/wCHCxMG2x7muysukzb230M6H09bX2Nna29zd3t/g4cAC5OXm5+jn3Ons7eba7vHt2fL16tj2+QL0+vXw/e7WAUwnrqDBgwgTKlzIsKHDh2gGSBwAccHEixAvaqTYcFCjRoYeNyoM6REhyZIHT4o0qPIjy5YTTcKUmHImx5cwE85cmJPnSYckK66sSAAj0aNIkypdyrSp06dQo0qdSrWq1atYs2rdyrWr169gwxZJAAA7) no-repeat center;background-size: 30% 30%;transition: all .3s;display:block;}.pdfjs .pdfViewer .pageContainer img.pdfLogo {position: absolute;z-index: 101;}.pdfjs .pdfViewer .pageContainer canvas.pdfLogo {position: absolute;z-index: 101;}.pdfjs .textLayer {position: absolute;left: 0;top: 0;right: 0;bottom: 0;overflow: hidden;opacity: 0.2;line-height: 1.0;z-index: 101;user-select: text;}.pdfjs .textLayer>span {color: transparent;position: absolute;white-space: pre;cursor: text;transform-origin: 0% 0%;}.pdfjs .textLayer .highlight {margin: -1px;padding: 1px;background-color: rgba(180, 0, 170, 1);border-radius: 4px;}.pdfjs .textLayer .highlight.begin {border-radius: 4px 0px 0px 4px;}.pdfjs .textLayer .highlight.end {border-radius: 0px 4px 4px 0px;}.pdfjs .textLayer .highlight.middle {border-radius: 0px;}.pdfjs .textLayer .highlight.selected {background-color: rgba(0, 100, 0, 1);}.pdfjs .textLayer ::selection {background: rgba(0, 0, 255, 0.3); }.pdfjs .textLayer .endOfContent {display: block;position: absolute;left: 0px;top: 100%;right: 0px;bottom: 0px;z-index: -1;cursor: default;}.pdfjs .textLayer .endOfContent.active {top: 0px;}';
  54. var buildElement = function (html) {
  55. var div = document.createElement('div');
  56. div.innerHTML = html;
  57. return div.firstChild;
  58. };
  59. var triggerEvent = function (el, name) {
  60. var event = document.createEvent('HTMLEvents');
  61. event.initEvent(name, true, false);
  62. el.dispatchEvent(event);
  63. };
  64. var definePinchZoom = function () {
  65. var PinchZoom = function (el, options, pinchParentNode) {
  66. this.el = el;
  67. this.pinchParentNode = pinchParentNode;
  68. this.zoomFactor = 1;
  69. this.lastScale = 1;
  70. this.offset = {
  71. x: 0,
  72. y: 0
  73. };
  74. this.options = Object.assign({}, this.defaults, options);
  75. this.options.tapZoomFactor = isNaN(options.tapZoomFactor) ? 2 : options.tapZoomFactor;
  76. this.options.zoomOutFactor = isNaN(options.zoomOutFactor) ? 1.2 : options.zoomOutFactor;
  77. this.options.animationDuration = isNaN(options.animationDuration) ? 300 : options.animationDuration;
  78. this.options.maxZoom = isNaN(options.maxZoom) ? 4 : options.maxZoom;
  79. this.options.minZoom = isNaN(options.minZoom) ? 0.5 : options.minZoom;
  80. this.options.dampingFactor = isNaN(options.dampingFactor) ? 0.85 : options.dampingFactor;
  81. this.setupMarkup();
  82. this.bindEvents();
  83. this.update();
  84. this.enable();
  85. this.height = 0;
  86. this.load = false;
  87. this.direction = null;
  88. this.clientY = null;
  89. this.lastclientY = null;
  90. this.lastOffsets = [];
  91. for (var i = 0; i < 5; i++) {
  92. this.lastOffsets.push({ x: 0, y: 0, time: Date.now() });
  93. }
  94. },
  95. sum = function (a, b) {
  96. return a + b;
  97. },
  98. isCloseTo = function (value, expected) {
  99. return value > expected - 0.01 && value < expected + 0.01;
  100. };
  101. PinchZoom.prototype = {
  102. defaults: {
  103. tapZoomFactor: 2,
  104. zoomOutFactor: 1.2,
  105. animationDuration: 300,
  106. maxZoom: 4,
  107. minZoom: 0.5,
  108. draggableUnzoomed: true,
  109. lockDragAxis: false,
  110. use2d: true,
  111. zoomStartEventName: 'pz_zoomstart',
  112. zoomEndEventName: 'pz_zoomend',
  113. dragStartEventName: 'pz_dragstart',
  114. dragEndEventName: 'pz_dragend',
  115. doubleTapEventName: 'pz_doubletap'
  116. },
  117. handleDragStart: function (event) {
  118. triggerEvent(this.el, this.options.dragStartEventName);
  119. this.stopAnimation();
  120. this.lastDragPosition = false;
  121. this.hasInteraction = true;
  122. this.handleDrag(event);
  123. },
  124. handleDrag: function (event) {
  125. if (this.zoomFactor > 1.0) {
  126. var touch = this.getTouches(event)[0];
  127. this.drag(touch, this.lastDragPosition, event);
  128. this.offset = this.sanitizeOffset(this.offset);
  129. this.lastDragPosition = touch;
  130. }
  131. },
  132. sanitizeOffset: function (offset) {
  133. var containerWidth = this.getContainerX();
  134. var imageWidth = this.el.offsetWidth * this.zoomFactor;
  135. var maxX = Math.max(imageWidth - containerWidth, 0);
  136. var containerHeight = this.getContainerY();
  137. var imageHeight = this.el.offsetHeight * this.zoomFactor;
  138. var maxY = Math.max(imageHeight - containerHeight, 0);
  139. var elasticFactor = 0.5;
  140. var newX = offset.x;
  141. var newY = offset.y;
  142. if (newX < 0) {
  143. newX = newX * elasticFactor;
  144. } else if (newX > maxX) {
  145. newX = maxX + (newX - maxX) * elasticFactor;
  146. }
  147. if (newY < 0) {
  148. newY = newY * elasticFactor;
  149. } else if (newY > maxY) {
  150. newY = maxY + (newY - maxY) * elasticFactor;
  151. }
  152. return {
  153. x: newX,
  154. y: newY
  155. };
  156. },
  157. handleDragEnd: function () {
  158. triggerEvent(this.el, this.options.dragEndEventName);
  159. this.end();
  160. var now = Date.now();
  161. var validRecords = this.lastOffsets.filter(r => now - r.time < 100);
  162. var velocityX = validRecords.length > 1 ?
  163. (validRecords[validRecords.length - 1].x - validRecords[0].x) /
  164. (validRecords[validRecords.length - 1].time - validRecords[0].time) : 0;
  165. this.startInertiaAnimation(velocityX);
  166. },
  167. startInertiaAnimation: function (initialVelocity) {
  168. var minVelocity = 0.08;
  169. var decay = 0.92;
  170. var animateFrame = () => {
  171. if (Math.abs(initialVelocity) < minVelocity) return;
  172. this.addOffset({
  173. x: initialVelocity * 16,
  174. y: 0
  175. });
  176. this.offset = this.sanitizeOffset(this.offset);
  177. this.update();
  178. initialVelocity *= decay;
  179. requestAnimationFrame(animateFrame);
  180. };
  181. requestAnimationFrame(animateFrame);
  182. },
  183. handleZoomStart: function (event) {
  184. triggerEvent(this.el, this.options.zoomStartEventName);
  185. this.stopAnimation();
  186. this.lastScale = 1;
  187. this.nthZoom = 0;
  188. this.lastZoomCenter = false;
  189. this.hasInteraction = true;
  190. },
  191. handleZoom: function (event, newScale) {
  192. var touchCenter = this.getTouchCenter(this.getTouches(event)),
  193. scale = newScale / this.lastScale;
  194. this.lastScale = newScale;
  195. this.nthZoom += 1;
  196. if (this.nthZoom > 3) {
  197. this.scale(scale, touchCenter);
  198. this.drag(touchCenter, this.lastZoomCenter);
  199. }
  200. this.lastZoomCenter = touchCenter;
  201. },
  202. handleZoomEnd: function () {
  203. triggerEvent(this.el, this.options.zoomEndEventName);
  204. this.end();
  205. },
  206. handleDoubleTap: function (event) {
  207. var center = this.getTouches(event)[0],
  208. zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor,
  209. startZoomFactor = this.zoomFactor,
  210. updateProgress = (function (progress) {
  211. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor),
  212. center);
  213. }).bind(this);
  214. if (this.hasInteraction) {
  215. return;
  216. }
  217. if (startZoomFactor > zoomFactor) {
  218. center = this.getCurrentZoomCenter();
  219. }
  220. this.animate(this.options.animationDuration, updateProgress, this.swing);
  221. triggerEvent(this.el, this.options.doubleTapEventName);
  222. },
  223. scaleTo: function (zoomFactor, center) {
  224. this.scale(zoomFactor / this.zoomFactor, center);
  225. },
  226. scale: function (scale, center) {
  227. scale = this.scaleZoomFactor(scale);
  228. this.addOffset({
  229. x: (scale - 1) * (center.x + this.offset.x),
  230. y: (scale - 1) * (center.y + this.offset.y)
  231. });
  232. this.done && this.done.call(this, this.getInitialZoomFactor() * this.zoomFactor);
  233. },
  234. scaleZoomFactor: function (scale) {
  235. var originalZoomFactor = this.zoomFactor;
  236. this.zoomFactor *= scale;
  237. this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom));
  238. return this.zoomFactor / originalZoomFactor;
  239. },
  240. canDrag: function () {
  241. return this.options.draggableUnzoomed || !isCloseTo(this.zoomFactor, 1);
  242. },
  243. drag: function (center, lastCenter, event) {
  244. if (!lastCenter) return;
  245. var dx = -(center.x - lastCenter.x) * this.options.dampingFactor;
  246. var dy = -(center.y - lastCenter.y) * this.options.dampingFactor;
  247. this.addOffset({ x: dx, y: dy });
  248. },
  249. getTouchCenter: function (touches) {
  250. return this.getVectorAvg(touches);
  251. },
  252. getVectorAvg: function (vectors) {
  253. return {
  254. x: vectors.map(function (v) {
  255. return v.x;
  256. }).reduce(sum) / vectors.length,
  257. y: vectors.map(function (v) {
  258. return v.y;
  259. }).reduce(sum) / vectors.length
  260. };
  261. },
  262. addOffset: function (offset) {
  263. this.offset = {
  264. x: this.offset.x + offset.x,
  265. y: this.offset.y + offset.y
  266. };
  267. },
  268. sanitize: function () {
  269. if (this.zoomFactor < this.options.zoomOutFactor) {
  270. this.zoomOutAnimation();
  271. } else if (this.isInsaneOffset(this.offset)) {
  272. this.sanitizeOffsetAnimation();
  273. }
  274. },
  275. isInsaneOffset: function (offset) {
  276. var sanitizedOffset = this.sanitizeOffset(offset);
  277. return sanitizedOffset.x !== offset.x ||
  278. sanitizedOffset.y !== offset.y;
  279. },
  280. sanitizeOffsetAnimation: function () {
  281. var targetOffset = this.sanitizeOffset(this.offset),
  282. startOffset = {
  283. x: this.offset.x,
  284. y: this.offset.y
  285. },
  286. updateProgress = (function (progress) {
  287. this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x);
  288. this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y);
  289. this.update();
  290. }).bind(this);
  291. this.animate(
  292. this.options.animationDuration,
  293. updateProgress,
  294. this.swing
  295. );
  296. },
  297. zoomOutAnimation: function () {
  298. var startZoomFactor = this.zoomFactor,
  299. zoomFactor = 1,
  300. center = this.getCurrentZoomCenter(),
  301. updateProgress = (function (progress) {
  302. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor),
  303. center);
  304. }).bind(this);
  305. this.animate(
  306. this.options.animationDuration,
  307. updateProgress,
  308. this.swing
  309. );
  310. },
  311. updateAspectRatio: function () {
  312. this.setContainerY(this.getContainerX() / this.getAspectRatio());
  313. },
  314. getInitialZoomFactor: function () {
  315. if (this.pinchContainer && this.el) {
  316. return this.pinchContainer.offsetWidth / this.el.offsetWidth;
  317. } else {
  318. return 0;
  319. }
  320. },
  321. getAspectRatio: function () {
  322. if (this.el) {
  323. return this.pinchContainer.offsetWidth / this.el.offsetHeight;
  324. } else {
  325. return 0;
  326. }
  327. },
  328. getCurrentZoomCenter: function () {
  329. var length = this.pinchContainer.offsetWidth * this.zoomFactor,
  330. offsetLeft = this.offset.x,
  331. offsetRight = length - offsetLeft - this.pinchContainer.offsetWidth,
  332. widthOffsetRatio = offsetLeft / offsetRight,
  333. centerX = widthOffsetRatio * this.pinchContainer.offsetWidth / (widthOffsetRatio + 1),
  334. height = this.pinchContainer.offsetHeight * this.zoomFactor,
  335. offsetTop = this.offset.y,
  336. offsetBottom = height - offsetTop - this.pinchContainer.offsetHeight,
  337. heightOffsetRatio = offsetTop / offsetBottom,
  338. centerY = heightOffsetRatio * this.pinchContainer.offsetHeight / (heightOffsetRatio + 1);
  339. if (offsetRight === 0) {
  340. centerX = this.pinchContainer.offsetWidth;
  341. }
  342. if (offsetBottom === 0) {
  343. centerY = this.pinchContainer.offsetHeight;
  344. }
  345. return {
  346. x: centerX,
  347. y: centerY
  348. };
  349. },
  350. getTouches: function (event) {
  351. var position = this.pinchContainer.getBoundingClientRect();
  352. var posTop = position.top;
  353. var posLeft = position.left;
  354. return Array.prototype.slice.call(event.touches).map(function (touch) {
  355. return {
  356. x: touch.pageX - posLeft,
  357. y: touch.pageY - posTop,
  358. };
  359. });
  360. },
  361. animate: function (duration, framefn, timefn, callback) {
  362. var startTime = new Date().getTime(),
  363. renderFrame = (function () {
  364. if (!this.inAnimation) {
  365. return;
  366. }
  367. var frameTime = new Date().getTime() - startTime,
  368. progress = frameTime / duration;
  369. if (frameTime >= duration) {
  370. framefn(1);
  371. if (callback) {
  372. callback();
  373. }
  374. this.update();
  375. this.stopAnimation();
  376. } else {
  377. if (timefn) {
  378. progress = timefn(progress);
  379. }
  380. framefn(progress);
  381. this.update();
  382. requestAnimationFrame(renderFrame);
  383. }
  384. }).bind(this);
  385. this.inAnimation = true;
  386. requestAnimationFrame(renderFrame);
  387. },
  388. stopAnimation: function () {
  389. this.inAnimation = false;
  390. },
  391. swing: function (p) {
  392. return -Math.cos(p * Math.PI) / 2 + 0.5;
  393. },
  394. getContainerX: function () {
  395. if (this.el) {
  396. return this.el.offsetWidth;
  397. } else {
  398. return 0;
  399. }
  400. },
  401. getContainerY: function () {
  402. return this.el.offsetHeight;
  403. },
  404. setContainerY: function (y) {
  405. y = y.toFixed(2);
  406. return this.pinchContainer.style.height = y + 'px';
  407. },
  408. setupMarkup: function () {
  409. this.pinchContainer = buildElement('<div class="pinch-zoom-container"></div>');
  410. this.el.parentNode.insertBefore(this.pinchContainer, this.el);
  411. this.pinchContainer.appendChild(this.el);
  412. this.pinchContainer.style.position = 'relative';
  413. this.el.style.webkitTransformOrigin = '0% 0%';
  414. this.el.style.mozTransformOrigin = '0% 0%';
  415. this.el.style.msTransformOrigin = '0% 0%';
  416. this.el.style.oTransformOrigin = '0% 0%';
  417. this.el.style.transformOrigin = '0% 0%';
  418. this.el.style.position = 'relative';
  419. },
  420. end: function () {
  421. this.hasInteraction = false;
  422. this.sanitize();
  423. this.update();
  424. },
  425. bindEvents: function () {
  426. var self = this;
  427. detectGestures(this.pinchParentNode, this);
  428. this.resizeHandler = this.update.bind(this);
  429. window.addEventListener('resize', this.resizeHandler);
  430. Array.from(this.el.querySelectorAll('canvas')).forEach(function (imgEl) {
  431. self.update.bind(self);
  432. });
  433. },
  434. update: function () {
  435. if (this.updatePlaned) {
  436. return;
  437. }
  438. this.updatePlaned = true;
  439. setTimeout((function () {
  440. this.updatePlaned = false;
  441. this.updateAspectRatio();
  442. var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor,
  443. offsetX = (-this.offset.x / zoomFactor).toFixed(3),
  444. offsetY = (-this.offset.y / zoomFactor).toFixed(3);
  445. this.lastclientY = offsetY;
  446. var transform3d = 'scale3d(' + zoomFactor + ', ' + zoomFactor + ',1) ' +
  447. 'translate3d(' + offsetX + 'px,' + offsetY + 'px,0px)',
  448. transform2d = 'scale(' + zoomFactor + ', ' + zoomFactor + ') ' +
  449. 'translate(' + offsetX + 'px,' + offsetY + 'px)',
  450. removeClone = (function () {
  451. if (this.clone) {
  452. this.clone.remove();
  453. delete this.clone;
  454. }
  455. }).bind(this);
  456. if (!this.options.use2d || this.hasInteraction || this.inAnimation) {
  457. this.is3d = true;
  458. this.el.style.webkitTransform = transform3d;
  459. this.el.style.mozTransform = transform2d;
  460. this.el.style.msTransform = transform2d;
  461. this.el.style.oTransform = transform2d;
  462. this.el.style.transform = transform3d;
  463. } else {
  464. this.el.style.webkitTransform = transform2d;
  465. this.el.style.mozTransform = transform2d;
  466. this.el.style.msTransform = transform2d;
  467. this.el.style.oTransform = transform2d;
  468. this.el.style.transform = transform2d;
  469. this.is3d = false;
  470. }
  471. this.lastOffsets.shift();
  472. this.lastOffsets.push({
  473. x: this.offset.x,
  474. y: this.offset.y,
  475. time: Date.now()
  476. });
  477. }).bind(this), 0);
  478. },
  479. enable: function () {
  480. this.enabled = true;
  481. },
  482. disable: function () {
  483. this.enabled = false;
  484. },
  485. destroy: function () {
  486. window.removeEventListener('resize', this.resizeHandler);
  487. if (this.pinchContainer) {
  488. var parentNode = this.pinchContainer.parentNode;
  489. var childNode = this.pinchContainer.firstChild;
  490. parentNode.appendChild(childNode);
  491. parentNode.removeChild(this.pinchContainer);
  492. }
  493. }
  494. };
  495. var detectGestures = function (el, target) {
  496. var interaction = null,
  497. fingers = 0,
  498. lastTouchStart = null,
  499. startTouches = null,
  500. lastTouchY = null,
  501. clientY = null,
  502. lastclientY = 0,
  503. lastTop = 0,
  504. setInteraction = function (newInteraction, event) {
  505. if (interaction !== newInteraction) {
  506. if (interaction && !newInteraction) {
  507. switch (interaction) {
  508. case "zoom":
  509. target.handleZoomEnd(event);
  510. break;
  511. case 'drag':
  512. target.handleDragEnd(event);
  513. break;
  514. }
  515. }
  516. switch (newInteraction) {
  517. case 'zoom':
  518. target.handleZoomStart(event);
  519. break;
  520. case 'drag':
  521. target.handleDragStart(event);
  522. break;
  523. }
  524. }
  525. interaction = newInteraction;
  526. },
  527. updateInteraction = function (event) {
  528. if (fingers === 2) {
  529. setInteraction('zoom');
  530. } else if (fingers === 1 && target.canDrag()) {
  531. setInteraction('drag', event);
  532. } else {
  533. setInteraction(null, event);
  534. }
  535. },
  536. targetTouches = function (touches) {
  537. return Array.prototype.slice.call(touches).map(function (touch) {
  538. return {
  539. x: touch.pageX,
  540. y: touch.pageY
  541. };
  542. });
  543. },
  544. getDistance = function (a, b) {
  545. var x, y;
  546. x = a.x - b.x;
  547. y = a.y - b.y;
  548. return Math.sqrt(x * x + y * y);
  549. },
  550. calculateScale = function (startTouches, endTouches) {
  551. var startDistance = getDistance(startTouches[0], startTouches[1]),
  552. endDistance = getDistance(endTouches[0], endTouches[1]);
  553. return endDistance / startDistance;
  554. },
  555. cancelEvent = function (event) {
  556. event.stopPropagation();
  557. event.preventDefault();
  558. },
  559. detectDoubleTap = function (event) {
  560. var time = (new Date()).getTime();
  561. var pageY = event.changedTouches[0].pageY;
  562. var top = el.scrollTop || 0;
  563. if (fingers > 1) {
  564. lastTouchStart = null;
  565. lastTouchY = null;
  566. cancelEvent(event);
  567. }
  568. if (time - lastTouchStart < 300 && Math.abs(pageY - lastTouchY) < 10 && Math.abs(lastTop - top) < 10) {
  569. cancelEvent(event);
  570. target.handleDoubleTap(event);
  571. switch (interaction) {
  572. case "zoom":
  573. target.handleZoomEnd(event);
  574. break;
  575. case 'drag':
  576. target.handleDragEnd(event);
  577. break;
  578. }
  579. }
  580. if (fingers === 1) {
  581. lastTouchStart = time;
  582. lastTouchY = pageY;
  583. lastTop = top;
  584. }
  585. },
  586. firstMove = true;
  587. el.addEventListener('touchstart', function (event) {
  588. if (target.enabled) {
  589. firstMove = true;
  590. fingers = event.touches.length;
  591. detectDoubleTap(event);
  592. clientY = event.changedTouches[0].clientY;
  593. if (fingers > 1) {
  594. cancelEvent(event);
  595. }
  596. }
  597. });
  598. el.addEventListener('touchmove', function (event) {
  599. if (target.enabled) {
  600. lastclientY = event.changedTouches[0].clientY;
  601. if (firstMove) {
  602. updateInteraction(event);
  603. startTouches = targetTouches(event.touches);
  604. } else {
  605. switch (interaction) {
  606. case 'zoom':
  607. target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches)));
  608. break;
  609. case 'drag':
  610. target.handleDrag(event);
  611. break;
  612. }
  613. if (interaction) {
  614. target.update(lastclientY);
  615. }
  616. }
  617. if (fingers > 1) {
  618. cancelEvent(event);
  619. }
  620. firstMove = false;
  621. }
  622. });
  623. el.addEventListener('touchend', function (event) {
  624. if (target.enabled) {
  625. fingers = event.touches.length;
  626. if (fingers > 1) {
  627. cancelEvent(event);
  628. }
  629. updateInteraction(event);
  630. }
  631. });
  632. };
  633. return PinchZoom;
  634. };
  635. var PinchZoom = definePinchZoom();
  636. var Pdfh5 = function (dom, options) {
  637. this.version = version;
  638. this.container = dom;
  639. this.options = options;
  640. this.thePDF = null;
  641. this.totalNum = null;
  642. this.pages = null;
  643. this.initTime = 0;
  644. this.currentNum = 1;
  645. this.loadedCount = 0;
  646. this.endTime = 0;
  647. this.pinchZoom = null;
  648. this.timer = null;
  649. this.docWidth = document.documentElement.clientWidth;
  650. this.cache = {};
  651. this.eventType = {};
  652. this.cacheNum = 1;
  653. this.resizeEvent = false;
  654. this.cacheData = null;
  655. this.pdfjsLibPromise = null;
  656. this.pdfjsLib = null;
  657. this.currentEditorMode = null;
  658. this.searchResults = [];
  659. this.currentSearchIndex = 0;
  660. this.currentSearchQuery = null;
  661. this.intersectionObserver = null;
  662. this.pageRenderStartTimes = {};
  663. this.isZooming = false;
  664. this.zoomDisabled = false;
  665. this.scrollDisabled = false;
  666. this.zoomConstraints = {
  667. minScale: 0.5,
  668. maxScale: 4.0,
  669. step: 0.1
  670. };
  671. // 沙箱管理
  672. this.sandboxManager = null;
  673. this.sandboxEnabled = true;
  674. // 密码保护相关
  675. this.passwordPrompt = null;
  676. // 分段加载配置
  677. this.progressiveLoading = false;
  678. this.chunkSize = 65536; // 64KB分块大小
  679. this.maxMemoryPages = 5; // 最大内存页面数
  680. this.loadedPages = new Map(); // 已加载页面缓存
  681. this.loadingQueue = []; // 加载队列
  682. this.memoryUsage = 0; // 内存使用量统计
  683. // 编辑器相关属性
  684. this.annotationEditorMode = 'NONE';
  685. this.editorParams = {
  686. freeTextColor: '#000000',
  687. freeTextSize: 12,
  688. inkColor: '#000000',
  689. inkThickness: 1,
  690. inkOpacity: 1,
  691. stampImage: null
  692. };
  693. this.editorAnnotations = []; // 存储所有注释
  694. this.isEditing = false;
  695. this.selectedAnnotation = null; // 当前选择的注释
  696. this.currentEditingTextInput = null; // 当前编辑的文本输入框
  697. // 墨迹绘制相关属性
  698. this.isDrawingInk = false;
  699. this.currentInkPath = null;
  700. // 注释编辑器UI管理器
  701. this.annotationEditorUIManager = null;
  702. this.init(options);
  703. };
  704. Pdfh5.prototype = {
  705. init: async function (options) {
  706. try {
  707. this.pdfjsLib = await initPdfJs();
  708. // 初始化沙箱管理器
  709. this.initSandbox();
  710. } catch (error) {
  711. console.error('Failed to initialize PDF.js:', error);
  712. return;
  713. }
  714. if (this.container.pdfLoaded) {
  715. this.destroy();
  716. }
  717. var $style = document.createElement('style');
  718. $style.type = 'text/css';
  719. $style.textContent = css;
  720. document.head.appendChild($style);
  721. this.container.pdfLoaded = false;
  722. this.container.classList.add("pdfjs");
  723. this.initTime = new Date().getTime();
  724. this.options = this.options ? this.options : {};
  725. this.options.pdfurl = this.options.pdfurl ? this.options.pdfurl : null;
  726. this.options.data = this.options.data ? this.options.data : null;
  727. this.options.scale = this.options.scale ? this.options.scale : 1;
  728. this.options.zoomEnable = this.options.zoomEnable === false ? false : true;
  729. this.options.scrollEnable = this.options.scrollEnable === false ? false : true;
  730. this.options.loadingBar = this.options.loadingBar === false ? false : true;
  731. this.options.pageNum = this.options.pageNum === false ? false : true;
  732. // 密码配置项
  733. this.options.password = this.options.password || null;
  734. this.options.backTop = this.options.backTop === false ? false : true;
  735. this.options.resize = this.options.resize === false ? false : true;
  736. this.options.textLayer = this.options.textLayer === true ? true : false;
  737. this.options.goto = isNaN(this.options.goto) ? 0 : this.options.goto;
  738. this.progressiveLoading = this.options.progressiveLoading === true ? true : false;
  739. if (this.options.chunkSize) this.chunkSize = this.options.chunkSize;
  740. if (this.options.maxMemoryPages) this.maxMemoryPages = this.options.maxMemoryPages;
  741. if (pdfjsLib && window._pdfh5ResourcePaths) {
  742. if (this.options.workerSrc) {
  743. pdfjsLib.GlobalWorkerOptions.workerSrc = this.options.workerSrc;
  744. }
  745. if (!this.options.cMapUrl) {
  746. this.options.cMapUrl = window._pdfh5ResourcePaths.cMapUrl;
  747. }
  748. if (!this.options.standardFontDataUrl) {
  749. this.options.standardFontDataUrl = window._pdfh5ResourcePaths.standardFontDataUrl;
  750. }
  751. if (!this.options.iccUrl) {
  752. this.options.iccUrl = window._pdfh5ResourcePaths.iccUrl;
  753. }
  754. if (!this.options.wasmUrl) {
  755. this.options.wasmUrl = window._pdfh5ResourcePaths.wasmUrl;
  756. }
  757. }
  758. this.createHTML();
  759. this.bindEvents();
  760. this.initEditorEvents();
  761. this.loadPDF();
  762. },
  763. createHTML: function () {
  764. var html = '';
  765. if (this.options.loadingBar) {
  766. html += '<div class="loadingBar">' +
  767. '<div class="progress">' +
  768. ' <div class="glimmer">' +
  769. '</div>' +
  770. ' </div>' +
  771. '</div>';
  772. }
  773. html += '<div class="pageNum">' +
  774. '<div class="pageNum-bg"></div>' +
  775. ' <div class="pageNum-num">' +
  776. ' <span class="pageNow">1</span>/' +
  777. '<span class="pageTotal">1</span>' +
  778. '</div>' +
  779. ' </div>' +
  780. '<div class="backTop">' +
  781. '</div>' +
  782. '<div class="loadEffect loading"></div>';
  783. this.container.innerHTML = html;
  784. var viewer = document.createElement("div");
  785. viewer.className = 'pdfViewer';
  786. var viewerContainer = document.createElement("div");
  787. viewerContainer.className = 'viewerContainer';
  788. viewerContainer.appendChild(viewer);
  789. this.container.appendChild(viewerContainer);
  790. this.viewer = viewer;
  791. this.viewerContainer = viewerContainer;
  792. this.pageNum = this.container.querySelector('.pageNum');
  793. this.pageNow = this.pageNum.querySelector('.pageNow');
  794. this.pageTotal = this.pageNum.querySelector('.pageTotal');
  795. this.loadingBar = this.container.querySelector('.loadingBar');
  796. if (this.loadingBar) {
  797. this.progress = this.loadingBar.querySelector('.progress');
  798. } else {
  799. this.progress = null;
  800. }
  801. this.backTop = this.container.querySelector('.backTop');
  802. this.loading = this.container.querySelector('.loading');
  803. },
  804. bindEvents: function () {
  805. var self = this;
  806. if (!this.options.scrollEnable) {
  807. this.viewerContainer.style.overflow = "hidden";
  808. } else {
  809. this.viewerContainer.style.overflow = "auto";
  810. }
  811. this.viewerContainer.addEventListener('scroll', function () {
  812. self.handleScroll();
  813. });
  814. this.backTop.addEventListener("click", function () {
  815. self.scrollToTop();
  816. });
  817. if (this.options.resize !== false) {
  818. var resizeTimeout;
  819. this.resizeHandler = function () {
  820. clearTimeout(resizeTimeout);
  821. resizeTimeout = setTimeout(function () {
  822. if (self.container.pdfLoaded && self.thePDF) {
  823. self.updateAllPagesScale();
  824. }
  825. }, 100);
  826. };
  827. window.addEventListener('resize', this.resizeHandler);
  828. }
  829. },
  830. updateAllPagesScale: function () {
  831. var self = this;
  832. if (!self.container.pdfLoaded || !self.thePDF) {
  833. return;
  834. }
  835. for (var pageNum = 1; pageNum <= self.totalNum; pageNum++) {
  836. var pageCache = self.cache[pageNum + ""];
  837. if (pageCache && pageCache.container && pageCache.container.querySelector('canvas')) {
  838. self.updatePageScale(pageNum);
  839. }
  840. }
  841. },
  842. updateVisiblePagesScale: function () {
  843. var self = this;
  844. if (!self.container.pdfLoaded || !self.thePDF) {
  845. return;
  846. }
  847. if (self.intersectionObserver) {
  848. var visiblePages = [];
  849. for (var pageNum = 1; pageNum <= self.totalNum; pageNum++) {
  850. var pageCache = self.cache[pageNum + ""];
  851. if (pageCache && pageCache.container) {
  852. var rect = pageCache.container.getBoundingClientRect();
  853. var containerRect = self.viewerContainer.getBoundingClientRect();
  854. if (rect.top < containerRect.bottom && rect.bottom > containerRect.top) {
  855. visiblePages.push(pageNum);
  856. }
  857. }
  858. }
  859. visiblePages.forEach(function (pageNum) {
  860. var pageCache = self.cache[pageNum + ""];
  861. if (pageCache && pageCache.container && pageCache.container.querySelector('canvas')) {
  862. self.updatePageScale(pageNum);
  863. }
  864. });
  865. } else {
  866. self.updateAllPagesScale();
  867. }
  868. },
  869. updatePageScale: function (pageNum) {
  870. var self = this;
  871. var pageCache = self.cache[pageNum + ""];
  872. if (!pageCache || !pageCache.container) {
  873. return;
  874. }
  875. if (self.loadingPages && self.loadingPages.has(pageNum)) {
  876. return;
  877. }
  878. if (!self.loadingPages) {
  879. self.loadingPages = new Set();
  880. }
  881. self.loadingPages.add(pageNum);
  882. self.thePDF.getPage(pageNum).then(function (page) {
  883. var userScale = self.scale || 1.0;
  884. if (userScale === 1.0) {
  885. var baseViewport = page.getViewport({ scale: 1.0 });
  886. var pageWidth = baseViewport.width;
  887. var pageHeight = baseViewport.height;
  888. var containerWidth = self.viewer.clientWidth || self.viewer.offsetWidth;
  889. var containerHeight = self.viewer.clientHeight || self.viewer.offsetHeight;
  890. // 修复PDF容器高度异常小的问题
  891. if (containerHeight < 100) {
  892. // 如果容器高度异常小,使用窗口高度减去一些边距
  893. containerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间
  894. }
  895. var SCROLLBAR_PADDING = 40;
  896. var VERTICAL_PADDING = 5;
  897. var hPadding = SCROLLBAR_PADDING;
  898. var vPadding = VERTICAL_PADDING;
  899. var pageWidthScaleFactor = 1;
  900. var currentPageScale = 1.0;
  901. var pageWidthScale = (containerWidth - hPadding) / pageWidth * currentPageScale / pageWidthScaleFactor;
  902. var pageHeightScale = (containerHeight - vPadding) / pageHeight * currentPageScale;
  903. var isPortrait = pageHeight > pageWidth;
  904. var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
  905. userScale = Math.min(1.25, horizontalScale);
  906. }
  907. self.renderPage(page, pageNum, { scale: userScale, forceRerender: true }, 100 / self.totalNum);
  908. var textLayerDiv = pageCache.container.querySelector('.textLayer');
  909. if (textLayerDiv && textLayerDiv.textLayer) {
  910. var PDF_TO_CSS_UNITS = 96.0 / 72.0; // 1.333...
  911. var newViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS });
  912. var pageWidth = newViewport.rawDims.pageWidth;
  913. var pageHeight = newViewport.rawDims.pageHeight;
  914. textLayerDiv.textLayer.update({
  915. viewport: newViewport
  916. });
  917. var w = 'var(--total-scale-factor) * ' + pageWidth + 'px';
  918. var h = 'var(--total-scale-factor) * ' + pageHeight + 'px';
  919. textLayerDiv.style.width = w;
  920. textLayerDiv.style.height = h;
  921. textLayerDiv.style.transform = 'none';
  922. textLayerDiv.style.transformOrigin = '0 0';
  923. var containerScale = pageCache.container.style.getPropertyValue('--scale-factor') || newViewport.scale;
  924. textLayerDiv.style.setProperty('--scale-factor', containerScale);
  925. textLayerDiv.style.setProperty('--user-unit', '1');
  926. textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))');
  927. }
  928. if (self.loadingPages) {
  929. self.loadingPages.delete(pageNum);
  930. }
  931. }).catch(function (error) {
  932. console.error('Error updating page scale:', error);
  933. if (self.loadingPages) {
  934. self.loadingPages.delete(pageNum);
  935. }
  936. });
  937. },
  938. getContainerWidth: function (container) {
  939. var current = container;
  940. while (current && current !== document.body) {
  941. var computedStyle = window.getComputedStyle(current);
  942. var width = computedStyle.width;
  943. var maxWidth = computedStyle.maxWidth;
  944. if (width !== 'auto' && width !== '100%' && width !== '100vw') {
  945. var widthValue = parseFloat(width);
  946. if (widthValue > 0) {
  947. return widthValue;
  948. }
  949. }
  950. if (maxWidth !== 'none' && maxWidth !== '100%' && maxWidth !== '100vw') {
  951. var maxWidthValue = parseFloat(maxWidth);
  952. if (maxWidthValue > 0) {
  953. return maxWidthValue;
  954. }
  955. }
  956. current = current.parentElement;
  957. }
  958. var docWidth = document.documentElement.clientWidth;
  959. return docWidth;
  960. },
  961. loadPDF: function () {
  962. var url = this.options.pdfurl;
  963. var data = this.options.data;
  964. if (url) {
  965. this.renderPdf(this.options, { url: url });
  966. } else if (data) {
  967. this.renderPdf(this.options, { data: data });
  968. } else {
  969. console.error("Expect options.pdfurl or options.data!");
  970. }
  971. },
  972. renderPdf: function (options, obj) {
  973. var self = this;
  974. this.container.pdfLoaded = true;
  975. // 字体配置
  976. obj.cMapUrl = options.cMapUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.cMapUrl : '../cmaps/');
  977. obj.standardFontDataUrl = options.standardFontDataUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.standardFontDataUrl : '../standard_fonts/');
  978. // 颜色管理
  979. obj.iccUrl = options.iccUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.iccUrl : '../iccs/');
  980. // 图片渲染
  981. obj.wasmUrl = options.wasmUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.wasmUrl : '../wasm/');
  982. obj.cMapPacked = options.cMapPacked !== false; // 默认启用压缩的CMap
  983. obj.disableFontFace = options.disableFontFace === true; // 默认不禁用字体
  984. obj.enableXfa = options.enableXfa !== false; // 默认启用XFA
  985. obj.enableHWA = options.enableHWA !== false; // 默认启用硬件加速,对图片渲染很重要
  986. obj.verbosity = options.verbosity || 1; // 设置详细程度
  987. if (options.httpHeaders) {
  988. obj.httpHeaders = options.httpHeaders;
  989. }
  990. if (options.withCredentials) {
  991. obj.withCredentials = true;
  992. }
  993. if (options.password) {
  994. obj.password = options.password;
  995. }
  996. // 分段加载配置
  997. if (self.progressiveLoading) {
  998. obj.disableStream = false;
  999. obj.disableAutoFetch = false;
  1000. obj.rangeChunkSize = self.chunkSize;
  1001. obj.maxImageSize = options.maxImageSize || 8388608; // 8388608最大图片大小,兼容iOS Safari
  1002. obj.canvasMaxAreaInBytes = options.canvasMaxAreaInBytes || 8388608; // 8388608最大canvas面积 iOS Safari浏览器canvas限制约为16777216
  1003. }
  1004. this.pdfjsLib.getDocument(obj).promise.then(function (pdf) {
  1005. self.loading.style.display = "none";
  1006. self.thePDF = pdf;
  1007. self.totalNum = pdf.numPages;
  1008. if (options.limit > 0) {
  1009. self.totalNum = options.limit;
  1010. }
  1011. self.pageTotal.innerText = self.totalNum;
  1012. if (!self.eventBus) {
  1013. self.eventBus = self;
  1014. }
  1015. // 初始化AnnotationEditorUIManager(
  1016. if (self.pdfjsLib.AnnotationEditorUIManager && self.thePDF.annotationStorage && self.eventBus) {
  1017. try {
  1018. self.annotationEditorUIManager = new self.pdfjsLib.AnnotationEditorUIManager(
  1019. self.container,
  1020. self.pdfViewer,
  1021. null, // viewerAlert
  1022. null, // altTextManager
  1023. null, // commentManager
  1024. null, // signatureManager
  1025. self.eventBus,
  1026. self.thePDF,
  1027. null, // pageColors
  1028. null, // highlightColors
  1029. false, // enableHighlightFloatingButton
  1030. false, // enableUpdatedAddImage
  1031. false, // enableNewAltTextWhenAddingImage
  1032. null, // mlManager
  1033. null, // editorUndoBar
  1034. false // supportsPinchToZoom
  1035. );
  1036. setTimeout(function () {
  1037. for (var i = 1; i <= self.totalNum; i++) {
  1038. self.createAnnotationEditorLayer(i);
  1039. }
  1040. }, 100);
  1041. } catch (error) {
  1042. }
  1043. }
  1044. if (self.thePDF && self.thePDF.annotationStorage) {
  1045. self.thePDF.annotationStorage.onSetModified = function () {
  1046. };
  1047. self.thePDF.annotationStorage.onResetModified = function () {
  1048. };
  1049. }
  1050. // 如果正在验证密码且PDF加载成功,隐藏密码框
  1051. if (self.passwordValidating) {
  1052. self.hidePasswordPrompt();
  1053. self.passwordValidating = false; // 重置标志
  1054. }
  1055. self.trigger('ready', { totalPages: self.totalNum });
  1056. if (self.progressiveLoading) {
  1057. self.initProgressiveLoading(pdf, options);
  1058. } else if (options.lazyLoad) {
  1059. // 懒加载模式:只渲染可见页面
  1060. self.initLazyLoading(pdf, options);
  1061. } else {
  1062. // 传统模式:渲染所有页面
  1063. self.renderAllPages(pdf, options);
  1064. }
  1065. }).catch(function (err) {
  1066. self.loading.style.display = "none";
  1067. // 处理密码错误
  1068. if (err.name === 'PasswordException') {
  1069. self.handlePasswordError(err);
  1070. } else {
  1071. console.error('PDF loading error:', err);
  1072. self.trigger('error', { message: 'PDF加载失败: ' + err.message });
  1073. }
  1074. });
  1075. },
  1076. renderAllPages: function (pdf, options) {
  1077. var self = this;
  1078. // 初始化缓存
  1079. for (var i = 1; i <= self.totalNum; i++) {
  1080. self.cache[i + ""] = {
  1081. page: null,
  1082. loaded: false,
  1083. container: null,
  1084. scaledViewport: null,
  1085. canvas: null,
  1086. imgWidth: null
  1087. };
  1088. }
  1089. // 初始化当前页码
  1090. self.currentNum = 1;
  1091. // 检查是否有goto配置
  1092. if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) {
  1093. self.currentNum = self.options.goto;
  1094. }
  1095. // 传统模式:一次性加载所有页面
  1096. var promise = Promise.resolve();
  1097. var num = Math.floor(100 / self.totalNum).toFixed(2);
  1098. // 渲染所有页面
  1099. for (var i = 1; i <= self.totalNum; i++) {
  1100. promise = promise.then(function (pageNum) {
  1101. return pdf.getPage(pageNum).then(function (page) {
  1102. return self.renderPage(page, pageNum, options, num);
  1103. });
  1104. }.bind(null, i));
  1105. }
  1106. return promise.then(function () {
  1107. // 初始化TouchManager - 更好的手势缩放
  1108. if (self.options.zoomEnable) {
  1109. self.initTouchManager();
  1110. }
  1111. });
  1112. },
  1113. // 预计算所有页面尺寸
  1114. preCalculateAllPageSizes: function (pdf, options) {
  1115. var self = this;
  1116. // 获取pdfViewer的实际尺寸
  1117. var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth;
  1118. var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight;
  1119. // 修复PDF容器高度异常小的问题
  1120. if (pdfViewerHeight < 100) {
  1121. pdfViewerHeight = window.innerHeight - 200;
  1122. }
  1123. // 计算pdfViewer的实际可用宽度(减去padding)
  1124. var pdfViewerStyle = window.getComputedStyle(self.viewer);
  1125. var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0;
  1126. var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0;
  1127. var availableWidth = pdfViewerWidth - paddingLeft - paddingRight;
  1128. // 预计算所有页面的尺寸
  1129. var pageSizePromises = [];
  1130. for (var i = 1; i <= self.totalNum; i++) {
  1131. pageSizePromises.push(
  1132. pdf.getPage(i).then(function (pageNum) {
  1133. return function (page) {
  1134. return self.calculatePageSize(page, pageNum, availableWidth, pdfViewerHeight, options);
  1135. };
  1136. }(i))
  1137. );
  1138. }
  1139. return Promise.all(pageSizePromises).then(function (pageSizes) {
  1140. // 存储所有页面的预计算尺寸
  1141. self.preCalculatedSizes = pageSizes;
  1142. return pageSizes;
  1143. });
  1144. },
  1145. // 计算单个页面的尺寸
  1146. calculatePageSize: function (page, pageNum, availableWidth, pdfViewerHeight, options) {
  1147. var self = this;
  1148. var PDF_TO_CSS_UNITS = 96.0 / 72.0;
  1149. var userScale = options.scale || 1.0;
  1150. // 获取PDF页面的基础尺寸
  1151. var baseViewport = page.getViewport({ scale: 1.0 });
  1152. var pageWidth = baseViewport.width;
  1153. var pageHeight = baseViewport.height;
  1154. // 如果用户没有指定缩放值,使用与renderPage完全一致的自动缩放逻辑
  1155. if (userScale === 1.0) {
  1156. // 使用与renderPage完全一致的缩放计算逻辑
  1157. var SCROLLBAR_PADDING = 40;
  1158. var VERTICAL_PADDING = 5;
  1159. var hPadding = SCROLLBAR_PADDING;
  1160. var vPadding = VERTICAL_PADDING;
  1161. // 计算页面宽度缩放
  1162. var pageWidthScale = (availableWidth - hPadding) / pageWidth;
  1163. var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight;
  1164. // 使用官方的"auto"模式逻辑,与renderPage保持一致
  1165. var isPortrait = pageHeight > pageWidth;
  1166. var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
  1167. userScale = Math.min(1.25, horizontalScale); // MAX_AUTO_SCALE = 1.25
  1168. // 设置合理的最小缩放限制,确保内容可见
  1169. userScale = Math.max(userScale, 0.2); // 最小20%
  1170. }
  1171. // 计算最终的viewport
  1172. var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS });
  1173. // 确保pageContainer宽度不超过pdfViewer的可用宽度
  1174. var finalWidth = Math.min(scaledViewport.width, availableWidth);
  1175. var finalHeight = scaledViewport.height;
  1176. // 如果宽度被限制,按比例调整高度
  1177. if (finalWidth < scaledViewport.width) {
  1178. var scaleRatio = finalWidth / scaledViewport.width;
  1179. finalHeight = scaledViewport.height * scaleRatio;
  1180. }
  1181. return {
  1182. pageNum: pageNum,
  1183. width: finalWidth,
  1184. height: finalHeight,
  1185. scale: userScale,
  1186. viewport: scaledViewport
  1187. };
  1188. },
  1189. // 懒加载初始化
  1190. initLazyLoading: function (pdf, options) {
  1191. var self = this;
  1192. // 初始化内存管理
  1193. self.loadedPages = new Map();
  1194. self.maxMemoryPages = self.options.maxMemoryPages || 5; // 默认最多保留5页
  1195. // 初始化缓存
  1196. for (var i = 1; i <= self.totalNum; i++) {
  1197. self.cache[i + ""] = {
  1198. page: null,
  1199. loaded: false,
  1200. container: null,
  1201. scaledViewport: null,
  1202. canvas: null,
  1203. imgWidth: null,
  1204. pageHeight: 0, // 存储页面实际高度
  1205. pageTop: 0 // 存储页面顶部位置
  1206. };
  1207. }
  1208. // 初始化当前页码
  1209. self.currentNum = 1;
  1210. // 预计算所有页面尺寸,然后创建容器
  1211. self.preCalculateAllPageSizes(pdf, options).then(function () {
  1212. // 使用预计算的尺寸创建页面容器占位符
  1213. for (var i = 1; i <= self.totalNum; i++) {
  1214. self.createPageContainerWithPreCalculatedSize(i, options);
  1215. }
  1216. // 继续原有的懒加载逻辑
  1217. self.continueLazyLoading(pdf, options);
  1218. });
  1219. },
  1220. // 使用预计算尺寸创建页面容器
  1221. createPageContainerWithPreCalculatedSize: function (pageNum, options) {
  1222. var self = this;
  1223. var container = document.createElement('div');
  1224. container.className = 'pageContainer pageContainer' + pageNum;
  1225. container.setAttribute('name', 'page=' + pageNum);
  1226. container.setAttribute('data-page', pageNum);
  1227. // 使用预计算的尺寸
  1228. if (self.preCalculatedSizes && self.preCalculatedSizes[pageNum - 1]) {
  1229. var pageSize = self.preCalculatedSizes[pageNum - 1];
  1230. container.style.width = pageSize.width + 'px';
  1231. container.style.height = pageSize.height + 'px';
  1232. container["data-scale"] = pageSize.width / pageSize.height;
  1233. // 设置CSS变量
  1234. container.style.setProperty('--scale-factor', pageSize.viewport.scale);
  1235. } else {
  1236. // 如果没有预计算尺寸,使用默认尺寸
  1237. container.style.width = '100%';
  1238. container.style.height = 'auto';
  1239. container["data-scale"] = 1.0;
  1240. }
  1241. self.cache[pageNum + ""].container = container;
  1242. self.viewer.appendChild(container);
  1243. },
  1244. // 继续懒加载逻辑
  1245. continueLazyLoading: function (pdf, options) {
  1246. var self = this;
  1247. // 立即渲染第一页(确保有内容显示)
  1248. if (self.totalNum > 0) {
  1249. // 检查是否有goto配置
  1250. var startPage = 1;
  1251. if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) {
  1252. startPage = self.options.goto;
  1253. self.currentNum = startPage;
  1254. }
  1255. self.renderPageLazy(pdf, startPage, options);
  1256. // 如果有goto配置,延迟滚动到指定页面
  1257. if (self.options.goto && self.options.goto > 1) {
  1258. setTimeout(function () {
  1259. self.scrollToPage(self.options.goto);
  1260. }, 100);
  1261. }
  1262. }
  1263. // 懒加载模式下隐藏loadingBar
  1264. self.hideLoadingBarForLazyLoading();
  1265. // 使用滚动事件监听器替代 IntersectionObserver
  1266. self.initScrollBasedLazyLoading(pdf, options);
  1267. // 初始化TouchManager
  1268. if (self.options.zoomEnable) {
  1269. self.initTouchManager();
  1270. }
  1271. },
  1272. // 懒加载模式下隐藏loadingBar
  1273. hideLoadingBarForLazyLoading: function () {
  1274. var self = this;
  1275. // 设置进度条为100%
  1276. if (self.options.loadingBar && self.progress) {
  1277. self.progress.style.width = "100%";
  1278. }
  1279. // 延迟隐藏loadingBar,确保用户能看到100%的进度
  1280. if (self.loadingBar) {
  1281. setTimeout(function () {
  1282. self.loadingBar.style.display = "none";
  1283. // 触发懒加载完成事件
  1284. var time = new Date().getTime();
  1285. self.endTime = time - self.initTime;
  1286. // 触发完成事件
  1287. var arr1 = self.eventType["complete"];
  1288. if (arr1 && arr1 instanceof Array) {
  1289. for (var i = 0; i < arr1.length; i++) {
  1290. arr1[i] && arr1[i].call(self, "success", "pdf懒加载初始化完成", self.endTime);
  1291. }
  1292. }
  1293. }, 300);
  1294. }
  1295. },
  1296. // 基于滚动位置的懒加载
  1297. initScrollBasedLazyLoading: function (pdf, options) {
  1298. var self = this;
  1299. // 获取所有页面的实际高度和位置
  1300. self.updatePagePositions();
  1301. // 防抖机制,避免频繁调用
  1302. var scrollTimeout = null;
  1303. self.viewerContainer.addEventListener('scroll', function () {
  1304. if (scrollTimeout) {
  1305. clearTimeout(scrollTimeout);
  1306. }
  1307. scrollTimeout = setTimeout(function () {
  1308. // 检查并加载可见页面
  1309. self.checkAndLoadVisiblePages(pdf, options);
  1310. // 懒加载模式也需要内存管理
  1311. if (self.loadedPages && self.loadedPages.size > self.maxMemoryPages) {
  1312. self.forceCleanupMemory();
  1313. }
  1314. }, 100); // 100ms防抖
  1315. });
  1316. // 初始检查
  1317. setTimeout(function () {
  1318. self.checkAndLoadVisiblePages(pdf, options);
  1319. }, 100);
  1320. },
  1321. // 更新页面位置信息
  1322. updatePagePositions: function () {
  1323. var self = this;
  1324. var currentTop = 0;
  1325. // 获取第一页的实际高度作为默认高度
  1326. var defaultPageHeight = 400; // 更小的默认高度
  1327. if (self.cache["1"] && self.cache["1"].loaded && self.cache["1"].scaledViewport) {
  1328. defaultPageHeight = self.cache["1"].scaledViewport.height;
  1329. }
  1330. for (var i = 1; i <= self.totalNum; i++) {
  1331. var container = self.cache[i + ""].container;
  1332. if (container) {
  1333. // 如果页面已加载,使用实际高度
  1334. if (self.cache[i + ""].loaded && self.cache[i + ""].scaledViewport) {
  1335. self.cache[i + ""].pageHeight = self.cache[i + ""].scaledViewport.height;
  1336. } else {
  1337. // 使用第一页的高度作为默认高度
  1338. self.cache[i + ""].pageHeight = defaultPageHeight;
  1339. }
  1340. self.cache[i + ""].pageTop = currentTop;
  1341. currentTop += self.cache[i + ""].pageHeight + 8; // 8px是页面间距
  1342. }
  1343. }
  1344. },
  1345. // 检查并加载可见页面
  1346. checkAndLoadVisiblePages: function (pdf, options) {
  1347. // 分段加载模式下,使用Intersection Observer处理页面加载
  1348. // 此方法仅在懒加载模式下使用
  1349. if (this.progressiveLoading) {
  1350. return;
  1351. }
  1352. var self = this;
  1353. var scrollTop = self.viewerContainer.scrollTop;
  1354. var containerHeight = self.viewerContainer.clientHeight;
  1355. // 只加载当前视口附近的页面,不要一次性加载太多
  1356. var buffer = 100; // 减小缓冲区域
  1357. var loadTop = scrollTop - buffer;
  1358. var loadBottom = scrollTop + containerHeight + buffer;
  1359. var loadedCount = 0;
  1360. var toLoad = []; // 收集需要加载的页面
  1361. for (var i = 1; i <= self.totalNum; i++) {
  1362. if (!self.cache[i + ""].loaded) {
  1363. var container = self.cache[i + ""].container;
  1364. if (container) {
  1365. // 使用DOM元素的实际位置
  1366. var containerRect = container.getBoundingClientRect();
  1367. var viewerRect = self.viewerContainer.getBoundingClientRect();
  1368. // 计算相对于滚动容器的位置
  1369. var pageTop = containerRect.top - viewerRect.top + scrollTop;
  1370. var pageBottom = pageTop + containerRect.height;
  1371. // 检查页面是否在加载范围内
  1372. if (pageBottom > loadTop && pageTop < loadBottom) {
  1373. toLoad.push(i);
  1374. }
  1375. }
  1376. } else {
  1377. loadedCount++;
  1378. }
  1379. }
  1380. // 只加载最接近视口的页面,避免一次性加载太多
  1381. if (toLoad.length > 0) {
  1382. // 按距离视口中心的距离排序
  1383. var center = scrollTop + containerHeight / 2;
  1384. toLoad.sort(function (a, b) {
  1385. var containerA = self.cache[a + ""].container;
  1386. var containerB = self.cache[b + ""].container;
  1387. var rectA = containerA.getBoundingClientRect();
  1388. var rectB = containerB.getBoundingClientRect();
  1389. var viewerRect = self.viewerContainer.getBoundingClientRect();
  1390. var posA = rectA.top - viewerRect.top + scrollTop;
  1391. var posB = rectB.top - viewerRect.top + scrollTop;
  1392. return Math.abs(posA - center) - Math.abs(posB - center);
  1393. });
  1394. // 只加载前2个最接近的页面
  1395. var maxLoad = Math.min(2, toLoad.length);
  1396. for (var j = 0; j < maxLoad; j++) {
  1397. var pageNum = toLoad[j];
  1398. self.renderPageLazy(pdf, pageNum, options);
  1399. }
  1400. }
  1401. },
  1402. // 创建页面容器占位符
  1403. createPageContainer: function (pageNum, options, page) {
  1404. var self = this;
  1405. var container = document.createElement('div');
  1406. container.className = 'pageContainer pageContainer' + pageNum;
  1407. container.setAttribute('name', 'page=' + pageNum);
  1408. container.setAttribute('data-page', pageNum);
  1409. // 使用与官方viewer.mjs相同的缩放因子和自动缩放逻辑
  1410. var PDF_TO_CSS_UNITS = 96.0 / 72.0;
  1411. var userScale = options.scale || 1.0;
  1412. // 如果用户没有指定缩放值,使用官方的"auto"模式
  1413. if (userScale === 1.0) {
  1414. // 使用默认的自动缩放值,这个值会在renderPage中重新计算
  1415. userScale = 1.0;
  1416. }
  1417. var effectiveScale = userScale * PDF_TO_CSS_UNITS;
  1418. // 设置容器占位尺寸 - 使用与renderPage相同的计算逻辑
  1419. if (page) {
  1420. // 获取PDF页面的基础尺寸
  1421. var baseViewport = page.getViewport({ scale: 1.0 });
  1422. var pageWidth = baseViewport.width;
  1423. var pageHeight = baseViewport.height;
  1424. // 获取pdfViewer的实际尺寸
  1425. var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth;
  1426. var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight;
  1427. // 修复PDF容器高度异常小的问题
  1428. if (pdfViewerHeight < 100) {
  1429. // 如果容器高度异常小,使用窗口高度减去一些边距
  1430. pdfViewerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间
  1431. }
  1432. // 计算pdfViewer的实际可用宽度(减去padding)
  1433. var pdfViewerStyle = window.getComputedStyle(self.viewer);
  1434. var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0;
  1435. var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0;
  1436. var availableWidth = pdfViewerWidth - paddingLeft - paddingRight;
  1437. // 计算缩放比例
  1438. var SCROLLBAR_PADDING = 40;
  1439. var VERTICAL_PADDING = 5;
  1440. var hPadding = SCROLLBAR_PADDING;
  1441. var vPadding = VERTICAL_PADDING;
  1442. var pageWidthScale = (availableWidth - hPadding) / pageWidth;
  1443. var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight;
  1444. var isPortrait = pageHeight > pageWidth;
  1445. var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
  1446. var userScale = Math.min(1.25, horizontalScale);
  1447. // 计算最终的viewport
  1448. var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS });
  1449. // 确保pageContainer宽度不超过pdfViewer的可用宽度
  1450. var finalWidth = Math.min(scaledViewport.width, availableWidth);
  1451. var finalHeight = scaledViewport.height;
  1452. // 如果宽度被限制,按比例调整高度
  1453. if (finalWidth < scaledViewport.width) {
  1454. var scaleRatio = finalWidth / scaledViewport.width;
  1455. finalHeight = scaledViewport.height * scaleRatio;
  1456. }
  1457. container.style.width = finalWidth + 'px';
  1458. container.style.height = finalHeight + 'px';
  1459. container["data-scale"] = finalWidth / finalHeight;
  1460. } else {
  1461. // 如果没有page对象,使用默认尺寸
  1462. container.style.width = '100%';
  1463. container.style.height = 'auto';
  1464. container["data-scale"] = 1.0;
  1465. }
  1466. // 设置与官方viewer.mjs一致的CSS变量
  1467. container.style.setProperty('--scale-factor', effectiveScale);
  1468. self.cache[pageNum + ""].container = container;
  1469. self.viewer.appendChild(container);
  1470. },
  1471. // 懒加载渲染页面
  1472. renderPageLazy: function (pdf, pageNum, options) {
  1473. var self = this;
  1474. if (self.cache[pageNum + ""].loaded) {
  1475. return Promise.resolve();
  1476. }
  1477. return pdf.getPage(pageNum).then(function (page) {
  1478. var num = Math.floor(100 / self.totalNum).toFixed(2);
  1479. return self.renderPage(page, pageNum, options, num);
  1480. }).then(function () {
  1481. // 懒加载模式下也需要更新loadedPages
  1482. if (self.loadedPages) {
  1483. self.loadedPages.set(pageNum, {
  1484. page: self.cache[pageNum + ""].page,
  1485. loaded: true,
  1486. loadTime: new Date().getTime(),
  1487. container: self.cache[pageNum + ""].container
  1488. });
  1489. }
  1490. }).catch(function (error) {
  1491. console.error('Failed to render page', pageNum, error);
  1492. });
  1493. },
  1494. renderPage: function (page, pageNum, options, progressNum) {
  1495. var self = this;
  1496. // 记录页面渲染开始时间
  1497. self.pageRenderStartTimes[pageNum] = new Date().getTime();
  1498. // 使用与官方viewer.mjs完全一致的缩放逻辑
  1499. var PDF_TO_CSS_UNITS = 96.0 / 72.0;
  1500. var userScale = options.scale || 1.0;
  1501. var baseViewport = page.getViewport({ scale: 1.0 });
  1502. // 如果用户没有指定缩放值,使用智能的"auto"模式
  1503. if (userScale === 1.0) {
  1504. // 获取页面基础尺寸
  1505. var pageWidth = baseViewport.width;
  1506. var pageHeight = baseViewport.height;
  1507. // 获取pdfViewer的实际尺寸(与createPageContainer保持一致)
  1508. var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth;
  1509. var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight;
  1510. // 修复PDF容器高度异常小的问题
  1511. if (pdfViewerHeight < 100) {
  1512. // 如果容器高度异常小,使用窗口高度减去一些边距
  1513. pdfViewerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间
  1514. }
  1515. // 计算pdfViewer的实际可用宽度(减去padding)
  1516. var pdfViewerStyle = window.getComputedStyle(self.viewer);
  1517. var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0;
  1518. var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0;
  1519. var availableWidth = pdfViewerWidth - paddingLeft - paddingRight;
  1520. // 使用统一的缩放计算逻辑
  1521. var SCROLLBAR_PADDING = 40;
  1522. var VERTICAL_PADDING = 5;
  1523. var hPadding = SCROLLBAR_PADDING;
  1524. var vPadding = VERTICAL_PADDING;
  1525. // 计算页面宽度缩放
  1526. var pageWidthScale = (availableWidth - hPadding) / pageWidth;
  1527. var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight;
  1528. // 使用官方的"auto"模式逻辑
  1529. var isPortrait = pageHeight > pageWidth;
  1530. var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
  1531. userScale = Math.min(1.25, horizontalScale); // MAX_AUTO_SCALE = 1.25
  1532. }
  1533. // 如果用户指定了缩放值(如1.5、2.0等),直接使用用户设置的值
  1534. var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS });
  1535. // 检查容器是否已存在(懒加载模式)
  1536. var container = self.cache[pageNum + ""].container;
  1537. if (!container) {
  1538. container = document.createElement('div');
  1539. container.className = 'pageContainer pageContainer' + pageNum;
  1540. container.setAttribute('name', 'page=' + pageNum);
  1541. self.viewer.appendChild(container);
  1542. }
  1543. var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth;
  1544. var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight;
  1545. var pdfViewerStyle = window.getComputedStyle(self.viewer);
  1546. var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0;
  1547. var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0;
  1548. var availableWidth = pdfViewerWidth - paddingLeft - paddingRight;
  1549. var finalWidth = Math.min(scaledViewport.width, availableWidth);
  1550. var finalHeight = scaledViewport.height;
  1551. if (finalWidth < scaledViewport.width) {
  1552. var scaleRatio = finalWidth / scaledViewport.width;
  1553. finalHeight = scaledViewport.height * scaleRatio;
  1554. }
  1555. container.style.width = finalWidth + 'px';
  1556. container.style.height = finalHeight + 'px';
  1557. container.style.setProperty('--scale-factor', scaledViewport.scale);
  1558. self.cache[pageNum + ""].container = container;
  1559. self.cache[pageNum + ""].scaledViewport = scaledViewport;
  1560. return self.renderCanvas(page, scaledViewport, pageNum, progressNum, container, options, scaledViewport);
  1561. },
  1562. renderCanvas: function (page, viewport, pageNum, num, container, options, scaledViewport) {
  1563. var self = this;
  1564. // 检查是否已经存在canvas,避免重复创建
  1565. var existingCanvas = container.querySelector('canvas');
  1566. if (existingCanvas) {
  1567. // 如果是resize更新,需要重新渲染canvas内容
  1568. if (options && options.forceRerender) {
  1569. // 移除旧的canvas
  1570. existingCanvas.remove();
  1571. } else {
  1572. // 普通情况,避免重复创建
  1573. return Promise.resolve();
  1574. }
  1575. }
  1576. var canvas = document.createElement("canvas");
  1577. // 根据 enableHWA 设置 willReadFrequently,这对图片渲染很重要
  1578. var enableHWA = this.options.enableHWA !== false;
  1579. var context = canvas.getContext('2d', {
  1580. alpha: false,
  1581. willReadFrequently: !enableHWA
  1582. });
  1583. // 使用设备像素比设置 Canvas 实际尺寸,提高清晰度
  1584. var devicePixelRatio = window.devicePixelRatio || 1;
  1585. // 根据pageContainer的实际尺寸重新计算viewport缩放比例
  1586. var containerWidth = container.clientWidth || container.offsetWidth;
  1587. var containerHeight = container.clientHeight || container.offsetHeight;
  1588. // 如果容器尺寸为0(初始渲染时),使用原始viewport
  1589. if (containerWidth === 0 || containerHeight === 0) {
  1590. var canvasWidth = viewport.width;
  1591. var canvasHeight = viewport.height;
  1592. var adjustedViewport = viewport;
  1593. } else {
  1594. // 计算缩放比例,确保完全显示在pageContainer内
  1595. var scaleX = containerWidth / viewport.width;
  1596. var scaleY = containerHeight / viewport.height;
  1597. var scale = Math.min(scaleX, scaleY); // 选择较小的缩放比例,确保完全显示
  1598. // 计算缩放后的canvas尺寸
  1599. var canvasWidth = viewport.width * scale;
  1600. var canvasHeight = viewport.height * scale;
  1601. // 重新计算viewport的缩放比例,让PDF内容也按比例缩小
  1602. var originalScale = viewport.scale;
  1603. var adjustedScale = originalScale * scale;
  1604. // 创建调整后的viewport,让PDF内容按比例渲染
  1605. var adjustedViewport = page.getViewport({ scale: adjustedScale });
  1606. }
  1607. // 设置canvas的实际尺寸(考虑设备像素比)
  1608. canvas.width = canvasWidth * devicePixelRatio;
  1609. canvas.height = canvasHeight * devicePixelRatio;
  1610. canvas.style.width = canvasWidth + 'px';
  1611. canvas.style.height = canvasHeight + 'px';
  1612. // 缩放上下文以匹配设备像素比
  1613. context.scale(devicePixelRatio, devicePixelRatio);
  1614. var renderObj = {
  1615. canvasContext: context,
  1616. viewport: adjustedViewport
  1617. };
  1618. return page.render(renderObj).promise.then(function () {
  1619. self.loadedCount++;
  1620. // 隐藏加载效果
  1621. var loadEffect = container.querySelector('.loadEffect');
  1622. if (loadEffect) {
  1623. loadEffect.style.display = "none";
  1624. }
  1625. canvas.className = "canvasImg" + pageNum;
  1626. container.appendChild(canvas);
  1627. // 页面渲染完成后,检查是否需要清理内存
  1628. if (self.loadedPages && self.loadedPages.size > self.maxMemoryPages) {
  1629. // 延迟清理,避免影响当前渲染
  1630. setTimeout(function () {
  1631. self.forceCleanupMemory();
  1632. }, 100);
  1633. }
  1634. // 标记页面为已加载
  1635. self.cache[pageNum + ""].loaded = true;
  1636. self.cache[pageNum + ""].page = page;
  1637. self.cache[pageNum + ""].canvas = canvas;
  1638. self.cache[pageNum + ""].scaledViewport = viewport;
  1639. // 更新页面位置信息(懒加载需要)
  1640. if (self.options.lazyLoad) {
  1641. self.updatePagePositions();
  1642. }
  1643. // 渲染文本层(如果启用)
  1644. if (self.options.textLayer) {
  1645. // textLayer使用原始的scaledViewport,不需要调整
  1646. self.renderTextLayer(page, pageNum, container, scaledViewport);
  1647. }
  1648. // 更新进度条(分段加载模式下不显示进度条)
  1649. if (self.options.loadingBar && self.progress && !self.progressiveLoading) {
  1650. self.progress.style.width = num * self.loadedCount + "%";
  1651. }
  1652. // 触发渲染事件
  1653. var time = new Date().getTime();
  1654. var totalTime = time - self.initTime; // 总耗时
  1655. var pageTime = time - (self.pageRenderStartTimes[pageNum] || self.initTime); // 单页耗时
  1656. var arr1 = self.eventType["render"];
  1657. if (arr1 && arr1 instanceof Array) {
  1658. for (var i = 0; i < arr1.length; i++) {
  1659. arr1[i] && arr1[i].call(self, pageNum, pageTime, totalTime, container);
  1660. }
  1661. }
  1662. // 检查是否所有页面都加载完成(分段加载模式下不触发)
  1663. if (!self.progressiveLoading && self.loadedCount === self.totalNum) {
  1664. self.finalRender(options);
  1665. }
  1666. });
  1667. },
  1668. // 渲染文本层 - 使用与Canvas完全相同的viewport
  1669. renderTextLayer: function (page, pageNum, container, viewport, options) {
  1670. var self = this;
  1671. // 设置默认的options
  1672. options = options || {};
  1673. // 获取canvas尺寸(如果提供)
  1674. var canvasWidth = options.canvasWidth;
  1675. var canvasHeight = options.canvasHeight;
  1676. // 验证参数
  1677. if (!container || typeof container.querySelector !== 'function') {
  1678. return;
  1679. }
  1680. // 检查是否已存在文本层
  1681. var existingTextLayer = container.querySelector(".textLayer");
  1682. if (existingTextLayer && !options.forceRerender) {
  1683. return;
  1684. }
  1685. // 如果是强制重新渲染,移除现有的textLayer
  1686. if (existingTextLayer && options.forceRerender) {
  1687. existingTextLayer.remove();
  1688. }
  1689. // 创建文本层容器 - 使用与官方viewer.mjs完全一致的样式
  1690. var textLayerDiv = document.createElement('div');
  1691. textLayerDiv.setAttribute('class', 'textLayer');
  1692. textLayerDiv.setAttribute('role', 'presentation');
  1693. textLayerDiv.setAttribute('aria-label', 'Text layer');
  1694. textLayerDiv.tabIndex = 0;
  1695. textLayerDiv.style.setProperty('--scale-factor', viewport.scale);
  1696. textLayerDiv.style.setProperty('--user-unit', '1');
  1697. textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))');
  1698. textLayerDiv.style.position = 'absolute';
  1699. textLayerDiv.style.inset = '0';
  1700. textLayerDiv.style.overflow = 'clip';
  1701. textLayerDiv.style.opacity = '1';
  1702. textLayerDiv.style.lineHeight = '1';
  1703. textLayerDiv.style.userSelect = 'text';
  1704. textLayerDiv.style.pointerEvents = 'auto';
  1705. textLayerDiv.style.cursor = 'text';
  1706. textLayerDiv.style.transformOrigin = '0 0';
  1707. textLayerDiv.style.zIndex = '101';
  1708. textLayerDiv.style.textAlign = 'initial';
  1709. textLayerDiv.style.textSizeAdjust = 'none';
  1710. textLayerDiv.style.forcedColorAdjust = 'none';
  1711. container.appendChild(textLayerDiv);
  1712. page.getTextContent({
  1713. includeMarkedContent: true,
  1714. disableNormalization: true
  1715. }).then(function (textContent) {
  1716. // 检查pdfjsLib是否可用
  1717. if (!self.pdfjsLib || !self.pdfjsLib.TextLayer) {
  1718. return;
  1719. }
  1720. // 注意:TextLayer内部会处理缩放,所以传递原始的viewport
  1721. // 对于密码保护PDF,确保viewport与容器尺寸匹配
  1722. var textLayerViewport = viewport;
  1723. if (self.options.password && container.clientWidth > 0 && container.clientHeight > 0) {
  1724. // 密码保护PDF重新加载后,需要重新计算viewport
  1725. var containerWidth = container.clientWidth;
  1726. var containerHeight = container.clientHeight;
  1727. var scaleX = containerWidth / viewport.width;
  1728. var scaleY = containerHeight / viewport.height;
  1729. var scale = Math.min(scaleX, scaleY);
  1730. // 创建新的viewport,确保与容器尺寸匹配
  1731. textLayerViewport = page.getViewport({ scale: viewport.scale * scale });
  1732. }
  1733. var textLayer = new self.pdfjsLib.TextLayer({
  1734. textContentSource: textContent,
  1735. container: textLayerDiv,
  1736. viewport: textLayerViewport,
  1737. textDivs: [],
  1738. textContentItemsStr: []
  1739. });
  1740. // 将textLayer对象存储到DOM元素上,方便后续访问
  1741. textLayerDiv.textLayer = textLayer;
  1742. // 渲染文本层
  1743. textLayer.render().then(function () {
  1744. // 根据pageContainer的实际尺寸来设置textLayer尺寸
  1745. var containerWidth = container.clientWidth || container.offsetWidth;
  1746. var containerHeight = container.clientHeight || container.offsetHeight;
  1747. // 如果容器尺寸为0(初始渲染时),使用viewport尺寸
  1748. if (containerWidth === 0 || containerHeight === 0) {
  1749. var pageWidth = viewport.rawDims.pageWidth;
  1750. var pageHeight = viewport.rawDims.pageHeight;
  1751. var w = 'var(--total-scale-factor) * ' + pageWidth + 'px';
  1752. var h = 'var(--total-scale-factor) * ' + pageHeight + 'px';
  1753. } else {
  1754. // 计算textLayer的缩放比例,确保完全显示在pageContainer内
  1755. var scaleX = containerWidth / viewport.width;
  1756. var scaleY = containerHeight / viewport.height;
  1757. var scale = Math.min(scaleX, scaleY);
  1758. // 计算缩放后的textLayer尺寸
  1759. var scaledWidth = viewport.width * scale;
  1760. var scaledHeight = viewport.height * scale;
  1761. var w = scaledWidth + 'px';
  1762. var h = scaledHeight + 'px';
  1763. }
  1764. textLayerDiv.style.width = w;
  1765. textLayerDiv.style.height = h;
  1766. // 移除手动transform,让CSS变量自动处理
  1767. textLayerDiv.style.transform = 'none';
  1768. textLayerDiv.style.transformOrigin = '0 0';
  1769. // 使用与pageContainer相同的scale,确保textLayer与pageContainer完全一致
  1770. // 从pageContainer获取--scale-factor值,确保一致性
  1771. var containerScale = container.style.getPropertyValue('--scale-factor') || viewport.scale;
  1772. textLayerDiv.style.setProperty('--scale-factor', containerScale);
  1773. textLayerDiv.style.setProperty('--user-unit', '1');
  1774. textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))');
  1775. // 处理密码保护PDF的markedContent包装层
  1776. // 移除class="markedContent"的包装,只保留role="presentation"的span
  1777. var markedContentSpans = textLayerDiv.querySelectorAll('.markedContent');
  1778. markedContentSpans.forEach(function (markedContentSpan) {
  1779. // 获取所有子元素
  1780. var children = Array.from(markedContentSpan.children);
  1781. // 将子元素移动到父级
  1782. children.forEach(function (child) {
  1783. markedContentSpan.parentNode.insertBefore(child, markedContentSpan);
  1784. });
  1785. // 移除空的markedContent包装
  1786. markedContentSpan.remove();
  1787. });
  1788. // 触发文本层渲染完成事件
  1789. if (self.eventType && self.eventType["textlayerrendered"]) {
  1790. var arr1 = self.eventType["textlayerrendered"];
  1791. if (arr1 && arr1 instanceof Array) {
  1792. for (var i = 0; i < arr1.length; i++) {
  1793. arr1[i] && arr1[i].call(self, {
  1794. source: self,
  1795. pageNumber: pageNum,
  1796. numTextDivs: textLayerDiv.children.length
  1797. });
  1798. }
  1799. }
  1800. }
  1801. }).catch(function (error) {
  1802. console.error('Text layer rendering error:', error);
  1803. });
  1804. }).catch(function (error) {
  1805. console.error('Text content extraction error:', error);
  1806. });
  1807. },
  1808. finalRender: function (options) {
  1809. var time = new Date().getTime();
  1810. var self = this;
  1811. if (self.options.loadingBar && self.progress) {
  1812. self.progress.style.width = "100%";
  1813. }
  1814. if (self.loadingBar) {
  1815. setTimeout(function () {
  1816. self.loadingBar.style.display = "none";
  1817. }, 300);
  1818. }
  1819. self.endTime = time - self.initTime;
  1820. // 触发完成事件
  1821. var arr1 = self.eventType["complete"];
  1822. if (arr1 && arr1 instanceof Array) {
  1823. for (var i = 0; i < arr1.length; i++) {
  1824. arr1[i] && arr1[i].call(self, "success", "pdf加载完成", self.endTime);
  1825. }
  1826. }
  1827. },
  1828. handleScroll: function () {
  1829. // 原有的滚动处理逻辑
  1830. var scrollTop = this.viewerContainer.scrollTop;
  1831. var containerH = this.container.offsetHeight;
  1832. var height = containerH * (1 / 3);
  1833. // 触发滚动事件
  1834. this.trigger('scroll', {
  1835. scrollTop: scrollTop,
  1836. currentNum: this.currentNum
  1837. });
  1838. if (scrollTop >= 150) {
  1839. if (this.options.backTop) {
  1840. this.backTop.style.display = "block";
  1841. }
  1842. } else {
  1843. if (this.options.backTop) {
  1844. this.backTop.style.display = "none";
  1845. }
  1846. }
  1847. // 显示页码
  1848. clearTimeout(this.timer);
  1849. if (this.options.pageNum && this.pageNum) {
  1850. this.pageNum.style.display = "block";
  1851. }
  1852. // 更新当前页码 - 修复懒加载模式下的页码计算
  1853. if (this.viewerContainer) {
  1854. this.pages = this.viewerContainer.querySelectorAll('.pageContainer');
  1855. }
  1856. if (this.pages) {
  1857. this.pages.forEach(function (obj, index) {
  1858. var rect = obj.getBoundingClientRect();
  1859. var top = rect.top;
  1860. var bottom = rect.bottom;
  1861. if (top <= height && bottom > height) {
  1862. // 获取页面的实际页码,而不是数组索引
  1863. var pageNum = parseInt(obj.getAttribute('name').split('=')[1]) || (index + 1);
  1864. if (this.options.pageNum) {
  1865. this.pageNow.innerText = pageNum;
  1866. }
  1867. // 只有当页码真正改变时才更新和触发事件
  1868. if (this.currentNum !== pageNum) {
  1869. this.currentNum = pageNum;
  1870. this.trigger('pageChanged', { pageNumber: pageNum });
  1871. }
  1872. }
  1873. }.bind(this));
  1874. }
  1875. // 处理特殊情况 - 修复懒加载模式下的页码计算
  1876. if (scrollTop === 0) {
  1877. if (this.options.pageNum) {
  1878. this.pageNow.innerText = 1;
  1879. }
  1880. if (this.currentNum !== 1) {
  1881. this.currentNum = 1;
  1882. this.trigger('pageChanged', { pageNumber: 1 });
  1883. }
  1884. } else {
  1885. // 在懒加载模式下,只有在真正滚动到底部且最后一页已渲染时才显示最后一页
  1886. var lastPageContainer = this.viewerContainer.querySelector('.pageContainer[name="page=' + this.totalNum + '"]');
  1887. if (lastPageContainer && this.cache[this.totalNum + ""].loaded) {
  1888. var lastRect = lastPageContainer.getBoundingClientRect();
  1889. var viewerRect = this.viewerContainer.getBoundingClientRect();
  1890. // 更严格的检查:最后一页必须完全在视口内,且滚动位置接近底部
  1891. var isLastPageVisible = lastRect.bottom <= viewerRect.bottom && lastRect.top >= viewerRect.top;
  1892. var isNearBottom = scrollTop + this.container.offsetHeight >= this.viewer.offsetHeight - 100; // 100px容差
  1893. var isLastPageLoaded = this.cache[this.totalNum + ""].loaded;
  1894. // 只有在所有条件都满足时才显示最后一页
  1895. if (isLastPageVisible && isNearBottom && isLastPageLoaded) {
  1896. // 添加额外的检查:确保没有其他页面在视口内
  1897. var hasOtherPagesInView = false;
  1898. for (var i = 1; i < this.totalNum; i++) {
  1899. var otherPage = this.viewerContainer.querySelector('.pageContainer[name="page=' + i + '"]');
  1900. if (otherPage && this.cache[i + ""].loaded) {
  1901. var otherRect = otherPage.getBoundingClientRect();
  1902. if (otherRect.bottom > viewerRect.top && otherRect.top < viewerRect.bottom) {
  1903. hasOtherPagesInView = true;
  1904. break;
  1905. }
  1906. }
  1907. }
  1908. if (!hasOtherPagesInView) {
  1909. if (this.options.pageNum) {
  1910. this.pageNow.innerText = this.totalNum;
  1911. }
  1912. if (this.currentNum !== this.totalNum) {
  1913. this.currentNum = this.totalNum;
  1914. this.trigger('pageChanged', { pageNumber: this.totalNum });
  1915. }
  1916. }
  1917. }
  1918. }
  1919. }
  1920. // 自动隐藏页码
  1921. this.timer = setTimeout(function () {
  1922. if (this.options.pageNum && this.pageNum) {
  1923. this.pageNum.style.display = "none";
  1924. }
  1925. }.bind(this), 1500);
  1926. },
  1927. scrollToTop: function () {
  1928. this.viewerContainer.scrollTo({
  1929. top: 0,
  1930. behavior: "smooth"
  1931. });
  1932. },
  1933. // 滚动到指定页面
  1934. scrollToPage: function (pageNum) {
  1935. var self = this;
  1936. if (pageNum < 1 || pageNum > self.totalNum) {
  1937. return;
  1938. }
  1939. // 确保页面已加载
  1940. if (!self.cache[pageNum + ""].loaded) {
  1941. // 如果页面未加载,先加载它
  1942. self.renderPageLazy(self.thePDF, pageNum, self.options);
  1943. }
  1944. // 计算页面位置并滚动
  1945. setTimeout(function () {
  1946. var container = self.cache[pageNum + ""].container;
  1947. if (container) {
  1948. var containerRect = container.getBoundingClientRect();
  1949. var viewerRect = self.viewerContainer.getBoundingClientRect();
  1950. var scrollTop = self.viewerContainer.scrollTop;
  1951. // 计算页面相对于滚动容器的位置
  1952. var pageTop = containerRect.top - viewerRect.top + scrollTop;
  1953. // 滚动到页面顶部
  1954. self.viewerContainer.scrollTo({
  1955. top: pageTop,
  1956. behavior: "smooth"
  1957. });
  1958. // 更新当前页码
  1959. self.currentNum = pageNum;
  1960. if (self.options.pageNum) {
  1961. self.pageNow.innerText = pageNum;
  1962. }
  1963. }
  1964. }, 50);
  1965. },
  1966. // 保持原有的其他方法...
  1967. show: function (callback) {
  1968. this.container.style.display = "block";
  1969. callback && callback.call(this);
  1970. },
  1971. hide: function (callback) {
  1972. this.container.style.display = "none";
  1973. callback && callback.call(this);
  1974. },
  1975. on: function (type, callback) {
  1976. if (this.eventType[type] && this.eventType[type] instanceof Array) {
  1977. this.eventType[type].push(callback);
  1978. } else {
  1979. this.eventType[type] = [callback];
  1980. }
  1981. },
  1982. off: function (type) {
  1983. if (type !== undefined) {
  1984. this.eventType[type] = [null];
  1985. } else {
  1986. for (var i in this.eventType) {
  1987. this.eventType[i] = [null];
  1988. }
  1989. }
  1990. },
  1991. trigger: function (event, data) {
  1992. if (this.eventType[event]) {
  1993. this.eventType[event].forEach(function (callback) {
  1994. callback(data);
  1995. });
  1996. }
  1997. },
  1998. // 创建注释编辑器层
  1999. createAnnotationEditorLayer: function (pageNum) {
  2000. var self = this;
  2001. if (!self.annotationEditorUIManager) {
  2002. return;
  2003. }
  2004. try {
  2005. // 检查页面缓存是否存在
  2006. if (!self.cache || !self.cache[pageNum + ""]) {
  2007. return;
  2008. }
  2009. // 获取页面容器
  2010. var pageContainer = self.cache[pageNum + ""].container;
  2011. if (!pageContainer) {
  2012. return;
  2013. }
  2014. // 创建注释编辑器层div
  2015. var editorLayerDiv = document.createElement('div');
  2016. editorLayerDiv.className = 'annotationEditorLayer';
  2017. editorLayerDiv.style.position = 'absolute';
  2018. editorLayerDiv.style.top = '0';
  2019. editorLayerDiv.style.left = '0';
  2020. editorLayerDiv.style.width = '100%';
  2021. editorLayerDiv.style.height = '100%';
  2022. editorLayerDiv.style.pointerEvents = 'none';
  2023. editorLayerDiv.style.zIndex = '1000';
  2024. // 添加到页面容器
  2025. pageContainer.appendChild(editorLayerDiv);
  2026. // 创建注释编辑器层
  2027. var editorLayer = new self.pdfjsLib.AnnotationEditorLayer({
  2028. uiManager: self.annotationEditorUIManager,
  2029. div: editorLayerDiv,
  2030. structTreeLayer: null,
  2031. accessibilityManager: null,
  2032. pageIndex: pageNum - 1,
  2033. viewport: self.cache[pageNum + ""].scaledViewport
  2034. });
  2035. // 将注释编辑器层添加到AnnotationEditorUIManager
  2036. self.annotationEditorUIManager.addLayer(editorLayer);
  2037. } catch (error) {
  2038. }
  2039. },
  2040. // 初始化手势缩放 - 使用老版本的PinchZoom实现
  2041. initTouchManager: function () {
  2042. var self = this;
  2043. // 销毁现有的PinchZoom
  2044. if (this.pinchZoom) {
  2045. this.pinchZoom.destroy();
  2046. this.pinchZoom = null;
  2047. }
  2048. // 确保viewerContainer存在
  2049. if (!this.viewerContainer) {
  2050. return;
  2051. }
  2052. // 创建新的PinchZoom - 使用老版本的实现
  2053. this.pinchZoom = new PinchZoom(this.viewer, {
  2054. tapZoomFactor: this.options.tapZoomFactor || 2,
  2055. zoomOutFactor: this.options.zoomOutFactor || 1.2,
  2056. animationDuration: this.options.animationDuration || 300,
  2057. maxZoom: this.zoomConstraints.maxScale,
  2058. minZoom: this.zoomConstraints.minScale
  2059. }, this.viewerContainer);
  2060. // 设置缩放完成回调
  2061. var timeout, firstZoom = true;
  2062. this.pinchZoom.done = function (scale) {
  2063. clearTimeout(timeout);
  2064. timeout = setTimeout(function () {
  2065. // 更新缩放值
  2066. self.scale = scale;
  2067. // 智能更新策略:根据是否启用懒加载决定更新方式
  2068. if (self.thePDF && self.container.pdfLoaded) {
  2069. if (self.lazyLoad || self.progressiveLoading) {
  2070. // 懒加载模式:只更新可见页面,避免重新加载所有页面
  2071. self.updateVisiblePagesScale();
  2072. } else {
  2073. // 普通模式:更新所有页面
  2074. self.updateAllPagesScale();
  2075. }
  2076. }
  2077. // 触发缩放事件
  2078. self.trigger('zoom', {
  2079. scale: scale
  2080. });
  2081. }, 100); // 减少延迟时间,提高响应性
  2082. if (scale == 1) {
  2083. if (self.viewerContainer) {
  2084. self.viewerContainer.style.webkitOverflowScrolling = "touch";
  2085. }
  2086. } else {
  2087. if (self.viewerContainer) {
  2088. self.viewerContainer.style.webkitOverflowScrolling = "auto";
  2089. }
  2090. }
  2091. };
  2092. },
  2093. // 更新缩放 - 参考PDF.js官方实现
  2094. updateZoom: function (newScale, origin) {
  2095. var self = this;
  2096. // 使用官方的方式设置CSS变量
  2097. var PDF_TO_CSS_UNITS = 96.0 / 72.0;
  2098. this.viewer.style.setProperty('--scale-factor', newScale * PDF_TO_CSS_UNITS);
  2099. // 计算缩放中心点
  2100. var centerX, centerY;
  2101. if (origin && origin.length >= 2) {
  2102. // 使用手势中心点
  2103. centerX = origin[0];
  2104. centerY = origin[1];
  2105. } else {
  2106. // 使用容器中心点
  2107. centerX = this.viewerContainer.clientWidth / 2;
  2108. centerY = this.viewerContainer.clientHeight / 2;
  2109. }
  2110. // 将屏幕坐标转换为相对于容器的坐标
  2111. var containerRect = this.viewerContainer.getBoundingClientRect();
  2112. var relativeX = centerX - containerRect.left;
  2113. var relativeY = centerY - containerRect.top;
  2114. // 应用缩放变换
  2115. this.viewer.style.transform = 'scale(' + newScale + ')';
  2116. this.viewer.style.transformOrigin = relativeX + 'px ' + relativeY + 'px';
  2117. // 添加过渡效果,使缩放更平滑
  2118. this.viewer.style.transition = 'transform 0.1s ease-out';
  2119. // 重新渲染所有页面以应用新的缩放
  2120. if (this.thePDF && this.container.pdfLoaded) {
  2121. this.updateAllPagesScale();
  2122. }
  2123. // 触发缩放事件
  2124. this.trigger('zoom', {
  2125. scale: newScale,
  2126. origin: origin,
  2127. centerX: relativeX,
  2128. centerY: relativeY
  2129. });
  2130. },
  2131. // ==================== 缩放控制API ====================
  2132. // 禁用/启用缩放手势
  2133. setZoomEnabled: function (enabled) {
  2134. this.zoomDisabled = !enabled;
  2135. return this;
  2136. },
  2137. // 获取缩放状态
  2138. isZoomEnabled: function () {
  2139. return !this.zoomDisabled;
  2140. },
  2141. // 禁用/启用滚动
  2142. setScrollEnabled: function (enabled) {
  2143. this.scrollDisabled = !enabled;
  2144. if (this.viewerContainer) {
  2145. this.viewerContainer.style.overflow = enabled ? 'auto' : 'hidden';
  2146. }
  2147. return this;
  2148. },
  2149. // 获取滚动状态
  2150. isScrollEnabled: function () {
  2151. return !this.scrollDisabled;
  2152. },
  2153. // 设置缩放约束
  2154. setZoomConstraints: function (constraints) {
  2155. if (constraints.minScale !== undefined) {
  2156. this.zoomConstraints.minScale = Math.max(0.1, constraints.minScale);
  2157. }
  2158. if (constraints.maxScale !== undefined) {
  2159. this.zoomConstraints.maxScale = Math.min(20.0, constraints.maxScale);
  2160. }
  2161. if (constraints.step !== undefined) {
  2162. this.zoomConstraints.step = Math.max(0.01, constraints.step);
  2163. }
  2164. return this;
  2165. },
  2166. // 获取缩放约束
  2167. getZoomConstraints: function () {
  2168. return Object.assign({}, this.zoomConstraints);
  2169. },
  2170. // 检查是否正在缩放
  2171. isZooming: function () {
  2172. return this.isZooming;
  2173. },
  2174. // ==================== 新增功能API ====================
  2175. // 页面跳转功能API
  2176. goToPage: function (pageNum) {
  2177. var self = this;
  2178. if (pageNum < 1 || pageNum > self.totalNum) {
  2179. return false;
  2180. }
  2181. self.currentNum = pageNum;
  2182. // 滚动到指定页面
  2183. var pageContainer = null;
  2184. // 分段加载模式:使用loadedPages
  2185. if (self.progressiveLoading && self.loadedPages.has(pageNum)) {
  2186. var pageData = self.loadedPages.get(pageNum);
  2187. pageContainer = pageData.container;
  2188. }
  2189. // 传统模式:使用cache
  2190. else if (self.cache[pageNum + ""]) {
  2191. pageContainer = self.cache[pageNum + ""].container;
  2192. }
  2193. // 如果页面未加载,先加载页面
  2194. if (!pageContainer) {
  2195. if (self.progressiveLoading) {
  2196. // 分段加载模式:异步加载页面
  2197. self.loadPageProgressive(pageNum, self.options).then(function () {
  2198. // 页面加载完成后滚动
  2199. setTimeout(function () {
  2200. self.scrollToPage(pageNum);
  2201. }, 100);
  2202. });
  2203. } else {
  2204. // 传统模式:直接渲染页面
  2205. self.renderPage(self.thePDF.getPage(pageNum), pageNum, self.options);
  2206. setTimeout(function () {
  2207. self.scrollToPage(pageNum);
  2208. }, 100);
  2209. }
  2210. } else {
  2211. // 页面已加载,直接滚动
  2212. pageContainer.scrollIntoView({
  2213. behavior: 'smooth',
  2214. block: 'start'
  2215. });
  2216. }
  2217. self.trigger('pageChanged', { pageNumber: pageNum });
  2218. return true;
  2219. },
  2220. nextPage: function () {
  2221. var self = this;
  2222. if (self.currentNum < self.totalNum) {
  2223. return self.goToPage(self.currentNum + 1);
  2224. }
  2225. return false;
  2226. },
  2227. prevPage: function () {
  2228. var self = this;
  2229. if (self.currentNum > 1) {
  2230. return self.goToPage(self.currentNum - 1);
  2231. }
  2232. return false;
  2233. },
  2234. // 缩放功能API
  2235. zoomIn: function () {
  2236. var self = this;
  2237. if (!self.thePDF) {
  2238. console.warn('No PDF loaded');
  2239. return false;
  2240. }
  2241. // 防抖机制
  2242. if (self.zoomTimeout) {
  2243. clearTimeout(self.zoomTimeout);
  2244. }
  2245. var newScale = Math.min(self.scale * 1.2, 4.0);
  2246. self.zoomTimeout = setTimeout(function () {
  2247. self.setScale(newScale);
  2248. }, 100);
  2249. return true;
  2250. },
  2251. zoomOut: function () {
  2252. var self = this;
  2253. if (!self.thePDF) {
  2254. console.warn('No PDF loaded');
  2255. return false;
  2256. }
  2257. // 防抖机制
  2258. if (self.zoomTimeout) {
  2259. clearTimeout(self.zoomTimeout);
  2260. }
  2261. var newScale = Math.max(self.scale / 1.2, 0.25);
  2262. self.zoomTimeout = setTimeout(function () {
  2263. self.setScale(newScale);
  2264. }, 100);
  2265. return true;
  2266. },
  2267. setScale: function (scale) {
  2268. var self = this;
  2269. if (!self.thePDF) {
  2270. console.warn('No PDF loaded');
  2271. return false;
  2272. }
  2273. if (typeof scale === 'string') {
  2274. // 处理特殊缩放模式
  2275. switch (scale) {
  2276. case 'auto':
  2277. scale = 1.0;
  2278. break;
  2279. case 'page-actual':
  2280. scale = 1.0;
  2281. break;
  2282. case 'page-fit':
  2283. // 使用PDF页面的实际尺寸计算缩放
  2284. var pageWidth = self.docWidth; // 这里应该使用PDF页面的实际宽度
  2285. scale = self.viewer.clientWidth / pageWidth;
  2286. break;
  2287. case 'page-width':
  2288. // 使用PDF页面的实际尺寸计算缩放
  2289. var pageWidth = self.docWidth; // 这里应该使用PDF页面的实际宽度
  2290. scale = self.viewer.clientWidth / pageWidth;
  2291. break;
  2292. default:
  2293. // 处理百分比字符串,如 "200%" -> 2.0
  2294. if (scale.includes('%')) {
  2295. scale = parseFloat(scale) / 100;
  2296. } else {
  2297. scale = parseFloat(scale) || 1.0;
  2298. }
  2299. }
  2300. }
  2301. // 使用配置的缩放约束
  2302. var minScale = this.zoomConstraints.minScale || 0.5;
  2303. var maxScale = this.zoomConstraints.maxScale || 4.0;
  2304. self.scale = Math.max(minScale, Math.min(maxScale, scale));
  2305. // 触发缩放事件
  2306. self.trigger('scaleChanged', { scale: self.scale });
  2307. // 重新渲染已加载的页面
  2308. if (self.thePDF) {
  2309. for (var i = 1; i <= self.totalNum; i++) {
  2310. if (self.cache[i + ""].loaded) {
  2311. self.thePDF.getPage(i).then(function (page) {
  2312. var pageNum = page.pageNumber;
  2313. // 使用与官方viewer.mjs相同的缩放因子
  2314. var PDF_TO_CSS_UNITS = 96.0 / 72.0;
  2315. var scaledViewport = page.getViewport({ scale: self.scale * PDF_TO_CSS_UNITS });
  2316. var container = self.cache[pageNum + ""].container;
  2317. if (container) {
  2318. // 更新容器尺寸
  2319. // 更新CSS变量
  2320. container.style.setProperty('--scale-factor', scaledViewport.scale);
  2321. // 重新渲染Canvas - 创建新的Canvas避免重复渲染错误
  2322. var canvas = container.querySelector('canvas');
  2323. if (canvas) {
  2324. // 取消之前的渲染任务
  2325. if (self.cache[pageNum + ""].renderTask) {
  2326. self.cache[pageNum + ""].renderTask.cancel();
  2327. }
  2328. // 创建新的Canvas避免重复渲染
  2329. var newCanvas = document.createElement('canvas');
  2330. newCanvas.width = scaledViewport.width;
  2331. newCanvas.height = scaledViewport.height;
  2332. newCanvas.style.width = scaledViewport.width + 'px';
  2333. newCanvas.style.height = scaledViewport.height + 'px';
  2334. // 替换旧的Canvas
  2335. canvas.parentNode.replaceChild(newCanvas, canvas);
  2336. var enableHWA = self.options.enableHWA !== false;
  2337. var renderObj = {
  2338. canvasContext: newCanvas.getContext('2d', {
  2339. alpha: false,
  2340. willReadFrequently: !enableHWA
  2341. }),
  2342. viewport: scaledViewport
  2343. };
  2344. // 存储渲染任务以便后续取消
  2345. self.cache[pageNum + ""].renderTask = page.render(renderObj);
  2346. }
  2347. // 重新渲染文本层
  2348. var textLayer = container.querySelector('.textLayer');
  2349. if (textLayer) {
  2350. textLayer.innerHTML = '';
  2351. self.renderTextLayer(page, pageNum, container, scaledViewport);
  2352. }
  2353. }
  2354. });
  2355. }
  2356. }
  2357. }
  2358. self.trigger('zoom', { scale: self.scale });
  2359. return true;
  2360. },
  2361. // 搜索功能API
  2362. searchText: function (query) {
  2363. var self = this;
  2364. if (!self.thePDF || !query) {
  2365. return false;
  2366. }
  2367. // 简单的文本搜索实现
  2368. self.currentSearchQuery = query;
  2369. self.searchResults = [];
  2370. // 遍历所有页面进行搜索
  2371. for (var i = 1; i <= self.totalNum; i++) {
  2372. var pageContainer = self.cache[i + ""];
  2373. if (pageContainer && pageContainer.container) {
  2374. var textLayer = pageContainer.container.querySelector('.textLayer');
  2375. if (textLayer) {
  2376. var text = textLayer.textContent.toLowerCase();
  2377. var queryLower = query.toLowerCase();
  2378. if (text.includes(queryLower)) {
  2379. self.searchResults.push(i);
  2380. }
  2381. }
  2382. }
  2383. }
  2384. self.trigger('search', {
  2385. query: query,
  2386. results: self.searchResults,
  2387. totalResults: self.searchResults.length
  2388. });
  2389. return self.searchResults.length > 0;
  2390. },
  2391. clearSearch: function () {
  2392. var self = this;
  2393. self.currentSearchQuery = null;
  2394. self.searchResults = [];
  2395. self.currentSearchIndex = 0;
  2396. // 清除高亮
  2397. var textLayers = self.container.querySelectorAll('.textLayer');
  2398. textLayers.forEach(function (layer) {
  2399. var highlights = layer.querySelectorAll('.highlight');
  2400. highlights.forEach(function (highlight) {
  2401. highlight.classList.remove('highlight');
  2402. });
  2403. });
  2404. self.trigger('searchCleared');
  2405. },
  2406. findNext: function () {
  2407. var self = this;
  2408. if (!self.searchResults || self.searchResults.length === 0) {
  2409. return false;
  2410. }
  2411. self.currentSearchIndex = (self.currentSearchIndex + 1) % self.searchResults.length;
  2412. var pageNum = self.searchResults[self.currentSearchIndex];
  2413. self.goToPage(pageNum);
  2414. self.trigger('findNext', {
  2415. pageNumber: pageNum,
  2416. index: self.currentSearchIndex,
  2417. total: self.searchResults.length
  2418. });
  2419. return true;
  2420. },
  2421. findPrevious: function () {
  2422. var self = this;
  2423. if (!self.searchResults || self.searchResults.length === 0) {
  2424. return false;
  2425. }
  2426. self.currentSearchIndex = self.currentSearchIndex === 0 ?
  2427. self.searchResults.length - 1 : self.currentSearchIndex - 1;
  2428. var pageNum = self.searchResults[self.currentSearchIndex];
  2429. self.goToPage(pageNum);
  2430. self.trigger('findPrevious', {
  2431. pageNumber: pageNum,
  2432. index: self.currentSearchIndex,
  2433. total: self.searchResults.length
  2434. });
  2435. return true;
  2436. },
  2437. // 打印功能API - 参考PDF.js官方实现
  2438. printPDF: function () {
  2439. var self = this;
  2440. if (!self.thePDF) {
  2441. console.warn('No PDF loaded');
  2442. return false;
  2443. }
  2444. // 触发打印事件
  2445. self.trigger('print');
  2446. // 创建打印容器
  2447. var printContainer = document.createElement('div');
  2448. printContainer.id = 'pdfh5-print-container';
  2449. printContainer.style.position = 'absolute';
  2450. printContainer.style.left = '-9999px';
  2451. printContainer.style.top = '-9999px';
  2452. printContainer.style.width = '210mm'; // A4宽度
  2453. printContainer.style.height = '297mm'; // A4高度
  2454. printContainer.style.background = 'white';
  2455. printContainer.style.padding = '0';
  2456. printContainer.style.margin = '0';
  2457. // 添加打印样式
  2458. var printStyle = document.createElement('style');
  2459. printStyle.textContent = `
  2460. @media print {
  2461. body * { visibility: hidden; }
  2462. #pdfh5-print-container, #pdfh5-print-container * { visibility: visible; }
  2463. #pdfh5-print-container { position: absolute; left: 0; top: 0; width: 100%; }
  2464. }
  2465. `;
  2466. document.head.appendChild(printStyle);
  2467. document.body.appendChild(printContainer);
  2468. // 渲染所有页面到打印容器
  2469. self.renderPagesForPrint(printContainer).then(function () {
  2470. // 延迟打印,确保页面渲染完成
  2471. setTimeout(function () {
  2472. window.print();
  2473. // 清理打印容器
  2474. setTimeout(function () {
  2475. if (printContainer.parentNode) {
  2476. printContainer.parentNode.removeChild(printContainer);
  2477. }
  2478. if (printStyle.parentNode) {
  2479. printStyle.parentNode.removeChild(printStyle);
  2480. }
  2481. }, 1000);
  2482. }, 500);
  2483. }).catch(function (error) {
  2484. console.error('Print rendering failed:', error);
  2485. // 清理打印容器
  2486. if (printContainer.parentNode) {
  2487. printContainer.parentNode.removeChild(printContainer);
  2488. }
  2489. if (printStyle.parentNode) {
  2490. printStyle.parentNode.removeChild(printStyle);
  2491. }
  2492. });
  2493. return true;
  2494. },
  2495. // 为打印渲染页面
  2496. renderPagesForPrint: function (printContainer) {
  2497. var self = this;
  2498. var promises = [];
  2499. // 为每一页创建打印内容
  2500. for (var i = 1; i <= self.totalNum; i++) {
  2501. promises.push(self.renderPageForPrint(i, printContainer));
  2502. }
  2503. return Promise.all(promises);
  2504. },
  2505. // 渲染单页用于打印
  2506. renderPageForPrint: function (pageNum, printContainer) {
  2507. var self = this;
  2508. return self.thePDF.getPage(pageNum).then(function (page) {
  2509. // 使用适合打印的缩放比例
  2510. var viewport = page.getViewport({ scale: 1.5 });
  2511. // 创建页面容器
  2512. var pageContainer = document.createElement('div');
  2513. pageContainer.style.width = viewport.width + 'px';
  2514. pageContainer.style.height = viewport.height + 'px';
  2515. pageContainer.style.margin = '0 auto 10px auto';
  2516. pageContainer.style.pageBreakAfter = 'always';
  2517. pageContainer.style.background = 'white';
  2518. pageContainer.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
  2519. // 创建canvas
  2520. var canvas = document.createElement('canvas');
  2521. // 打印时使用标准像素比
  2522. canvas.width = viewport.width;
  2523. canvas.height = viewport.height;
  2524. canvas.style.width = '100%';
  2525. canvas.style.height = '100%';
  2526. pageContainer.appendChild(canvas);
  2527. printContainer.appendChild(pageContainer);
  2528. // 渲染页面
  2529. var enableHWA = self.options.enableHWA !== false;
  2530. var renderContext = {
  2531. canvasContext: canvas.getContext('2d', {
  2532. alpha: false,
  2533. willReadFrequently: !enableHWA
  2534. }),
  2535. viewport: viewport
  2536. };
  2537. return page.render(renderContext).promise;
  2538. });
  2539. },
  2540. // 渐进式加载初始化
  2541. initProgressiveLoading: function (pdf, options) {
  2542. var self = this;
  2543. // 初始化缓存
  2544. for (var i = 1; i <= self.totalNum; i++) {
  2545. self.cache[i + ""] = {
  2546. page: null,
  2547. loaded: false,
  2548. container: null,
  2549. scaledViewport: null,
  2550. canvas: null,
  2551. imgWidth: null
  2552. };
  2553. }
  2554. // 预计算所有页面尺寸,然后创建容器
  2555. self.preCalculateAllPageSizes(pdf, options).then(function () {
  2556. // 使用预计算的尺寸创建页面容器占位符
  2557. for (var i = 1; i <= self.totalNum; i++) {
  2558. self.createPageContainerWithPreCalculatedSize(i, options);
  2559. }
  2560. // 继续原有的分段加载逻辑
  2561. self.continueProgressiveLoading(pdf, options);
  2562. });
  2563. },
  2564. // 分段加载模式下隐藏loadingBar
  2565. hideLoadingBarForProgressiveLoading: function () {
  2566. var self = this;
  2567. // 设置进度条为100%
  2568. if (self.options.loadingBar && self.progress) {
  2569. self.progress.style.width = "100%";
  2570. }
  2571. // 延迟隐藏loadingBar,确保用户能看到100%的进度
  2572. if (self.loadingBar) {
  2573. setTimeout(function () {
  2574. self.loadingBar.style.display = "none";
  2575. // 触发分段加载完成事件
  2576. var time = new Date().getTime();
  2577. self.endTime = time - self.initTime;
  2578. // 触发完成事件
  2579. var arr1 = self.eventType["complete"];
  2580. if (arr1 && arr1 instanceof Array) {
  2581. for (var i = 0; i < arr1.length; i++) {
  2582. arr1[i] && arr1[i].call(self, "success", "pdf分段加载初始化完成", self.endTime);
  2583. }
  2584. }
  2585. }, 300);
  2586. }
  2587. },
  2588. // 继续分段加载逻辑
  2589. continueProgressiveLoading: function (pdf, options) {
  2590. var self = this;
  2591. // 检查是否有goto配置
  2592. if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) {
  2593. self.currentNum = self.options.goto;
  2594. }
  2595. // 分段加载模式下隐藏loadingBar
  2596. self.hideLoadingBarForProgressiveLoading();
  2597. // 先加载前几页,优先加载goto页面
  2598. var initialPages = Math.min(self.maxMemoryPages, self.totalNum);
  2599. var pagesToLoad = [];
  2600. // 如果有goto配置,优先加载goto页面及其周围页面
  2601. if (self.options.goto && self.options.goto > 0) {
  2602. var gotoPage = self.options.goto;
  2603. pagesToLoad.push(gotoPage);
  2604. // 加载goto页面周围的页面
  2605. for (var i = 1; i <= initialPages; i++) {
  2606. if (i !== gotoPage && Math.abs(i - gotoPage) <= Math.floor(initialPages / 2)) {
  2607. pagesToLoad.push(i);
  2608. }
  2609. }
  2610. } else {
  2611. // 没有goto配置,只加载第一页
  2612. pagesToLoad.push(1);
  2613. }
  2614. // 加载页面
  2615. pagesToLoad.forEach(function (pageNum) {
  2616. self.loadPageProgressive(pageNum, options);
  2617. });
  2618. // 设置滚动监听,动态加载页面
  2619. self.setupProgressiveScrollListener(options);
  2620. // 初始化TouchManager - 手势缩放
  2621. if (self.options.zoomEnable) {
  2622. self.initTouchManager();
  2623. }
  2624. // 延迟检查内存使用情况,确保内存管理正常工作
  2625. setTimeout(function () {
  2626. if (self.loadedPages.size > self.maxMemoryPages) {
  2627. self.cleanupDistantPages(self.currentNum);
  2628. }
  2629. }, 1000);
  2630. // 如果有goto配置,延迟滚动到指定页面
  2631. if (self.options.goto && self.options.goto > 1) {
  2632. setTimeout(function () {
  2633. self.scrollToPage(self.options.goto);
  2634. }, 200); // 稍微延迟,确保页面已加载
  2635. }
  2636. },
  2637. // 渐进式页面加载
  2638. loadPageProgressive: function (pageNum, options) {
  2639. var self = this;
  2640. // 检查是否已加载
  2641. if (self.loadedPages.has(pageNum)) {
  2642. return Promise.resolve();
  2643. }
  2644. // 限制同时加载的页面数量
  2645. if (self.loadingQueue.length >= 2) {
  2646. return Promise.resolve();
  2647. }
  2648. // 强制内存管理:如果超过最大页面数,清理最远的页面
  2649. if (self.loadedPages.size >= self.maxMemoryPages) {
  2650. self.cleanupDistantPages(pageNum);
  2651. }
  2652. // 添加到加载队列
  2653. self.loadingQueue.push(pageNum);
  2654. // 使用现有的renderPageLazy方法
  2655. return self.renderPageLazy(self.thePDF, pageNum, options).then(function () {
  2656. // 标记为已加载
  2657. self.loadedPages.set(pageNum, {
  2658. page: self.cache[pageNum + ""].page,
  2659. loaded: true,
  2660. loadTime: new Date().getTime(),
  2661. container: self.cache[pageNum + ""].container
  2662. });
  2663. // 从队列中移除
  2664. var index = self.loadingQueue.indexOf(pageNum);
  2665. if (index > -1) {
  2666. self.loadingQueue.splice(index, 1);
  2667. }
  2668. // 加载完成后再次检查内存使用情况
  2669. if (self.loadedPages.size > self.maxMemoryPages) {
  2670. self.cleanupDistantPages(pageNum);
  2671. }
  2672. // 触发页面加载完成事件
  2673. self.trigger('pageLoaded', {
  2674. pageNum: pageNum,
  2675. memoryUsage: self.getMemoryUsage()
  2676. });
  2677. }).catch(function (error) {
  2678. console.error('分段加载 - 页面加载失败:', pageNum, error);
  2679. });
  2680. },
  2681. // 设置渐进式滚动监听
  2682. setupProgressiveScrollListener: function (options) {
  2683. var self = this;
  2684. // 使用Intersection Observer监听页面可见性
  2685. if (self.intersectionObserver) {
  2686. self.intersectionObserver.disconnect();
  2687. }
  2688. self.intersectionObserver = new IntersectionObserver(function (entries) {
  2689. entries.forEach(function (entry) {
  2690. if (entry.isIntersecting) {
  2691. var pageNum = parseInt(entry.target.getAttribute('data-page'));
  2692. var intersectionRatio = entry.intersectionRatio;
  2693. // 只有当页面真正可见时才加载(可见比例大于0.3)
  2694. if (pageNum && !self.loadedPages.has(pageNum) && intersectionRatio > 0.3) {
  2695. self.loadPageProgressive(pageNum, options);
  2696. }
  2697. }
  2698. });
  2699. }, {
  2700. rootMargin: '0px', // 不提前加载
  2701. threshold: [0.3, 0.5, 1.0] // 提高可见性阈值
  2702. });
  2703. // 监听所有页面容器
  2704. for (var i = 1; i <= self.totalNum; i++) {
  2705. var container = self.cache[i + ""].container;
  2706. if (container) {
  2707. self.intersectionObserver.observe(container);
  2708. }
  2709. }
  2710. // 添加滚动事件监听器,在滚动时自动清理内存
  2711. if (self.scrollListener) {
  2712. self.viewerContainer.removeEventListener('scroll', self.scrollListener);
  2713. }
  2714. self.scrollListener = function () {
  2715. // 延迟执行,避免频繁清理
  2716. clearTimeout(self.scrollTimeout);
  2717. self.scrollTimeout = setTimeout(function () {
  2718. if (self.loadedPages.size > self.maxMemoryPages) {
  2719. self.forceCleanupMemory();
  2720. }
  2721. }, 500); // 500ms延迟
  2722. };
  2723. self.viewerContainer.addEventListener('scroll', self.scrollListener);
  2724. },
  2725. // 清理远距离页面
  2726. cleanupDistantPages: function (currentPage) {
  2727. var self = this;
  2728. var pagesToRemove = [];
  2729. // 如果已加载页面数超过最大限制,清理最远的页面
  2730. if (self.loadedPages.size >= self.maxMemoryPages) {
  2731. // 按距离排序,保留最近的页面
  2732. var sortedPages = Array.from(self.loadedPages.keys()).sort(function (a, b) {
  2733. return Math.abs(a - currentPage) - Math.abs(b - currentPage);
  2734. });
  2735. // 保留最近的maxMemoryPages个页面,清理其余的
  2736. var pagesToKeep = sortedPages.slice(0, self.maxMemoryPages);
  2737. sortedPages.forEach(function (pageNum) {
  2738. if (pagesToKeep.indexOf(pageNum) === -1) {
  2739. pagesToRemove.push(pageNum);
  2740. }
  2741. });
  2742. }
  2743. // 清理页面
  2744. pagesToRemove.forEach(function (pageNum) {
  2745. self.cleanupPage(pageNum);
  2746. });
  2747. },
  2748. // 清理单个页面
  2749. cleanupPage: function (pageNum) {
  2750. var self = this;
  2751. var container = self.cache[pageNum + ""].container;
  2752. if (container) {
  2753. // 只清理canvas和文本层,保留容器占位
  2754. var canvas = container.querySelector('canvas');
  2755. var textLayer = container.querySelector('.textLayer');
  2756. if (canvas) {
  2757. // 清空canvas内容
  2758. var enableHWA = self.options.enableHWA !== false;
  2759. var ctx = canvas.getContext('2d', {
  2760. alpha: false,
  2761. willReadFrequently: !enableHWA
  2762. });
  2763. ctx.clearRect(0, 0, canvas.width, canvas.height);
  2764. canvas.width = 0;
  2765. canvas.height = 0;
  2766. canvas.remove();
  2767. }
  2768. if (textLayer) {
  2769. textLayer.innerHTML = '';
  2770. textLayer.remove();
  2771. }
  2772. // 从缓存中移除
  2773. self.loadedPages.delete(pageNum);
  2774. // 从加载状态中移除(如果存在)
  2775. if (self.loadingPages) {
  2776. self.loadingPages.delete(pageNum);
  2777. }
  2778. // 重置缓存状态
  2779. if (self.cache[pageNum + ""]) {
  2780. self.cache[pageNum + ""].loaded = false;
  2781. self.cache[pageNum + ""].canvas = null;
  2782. self.cache[pageNum + ""].page = null;
  2783. }
  2784. // 触发页面清理事件
  2785. self.trigger('pageCleaned', {
  2786. pageNum: pageNum,
  2787. memoryUsage: self.getMemoryUsage()
  2788. });
  2789. }
  2790. },
  2791. // 获取内存使用情况
  2792. getMemoryUsage: function () {
  2793. var self = this;
  2794. var usage = {
  2795. loadedPages: self.loadedPages.size,
  2796. maxPages: self.maxMemoryPages,
  2797. loadingQueue: self.loadingQueue.length,
  2798. memoryUsage: self.memoryUsage
  2799. };
  2800. return usage;
  2801. },
  2802. // 强制清理内存
  2803. forceCleanupMemory: function () {
  2804. var self = this;
  2805. // 获取当前可见的页面
  2806. var visiblePages = [];
  2807. for (var i = 1; i <= self.totalNum; i++) {
  2808. var container = self.cache[i + ""].container;
  2809. if (container) {
  2810. var rect = container.getBoundingClientRect();
  2811. var viewerRect = self.viewerContainer.getBoundingClientRect();
  2812. // 检查页面是否在视口内(更严格的检查)
  2813. var isVisible = rect.bottom > viewerRect.top &&
  2814. rect.top < viewerRect.bottom &&
  2815. rect.width > 0 &&
  2816. rect.height > 0;
  2817. if (isVisible) {
  2818. visiblePages.push(i);
  2819. }
  2820. }
  2821. }
  2822. // 如果已加载页面数超过限制,清理最远的页面
  2823. if (self.loadedPages.size > self.maxMemoryPages) {
  2824. // 按距离当前可见页面中心的距离排序
  2825. var centerPage = visiblePages.length > 0 ?
  2826. visiblePages[Math.floor(visiblePages.length / 2)] :
  2827. self.currentNum;
  2828. var sortedPages = Array.from(self.loadedPages.keys()).sort(function (a, b) {
  2829. return Math.abs(a - centerPage) - Math.abs(b - centerPage);
  2830. });
  2831. // 保留最近的maxMemoryPages个页面
  2832. var pagesToKeep = sortedPages.slice(0, self.maxMemoryPages);
  2833. var pagesToRemove = [];
  2834. sortedPages.forEach(function (pageNum) {
  2835. if (pagesToKeep.indexOf(pageNum) === -1) {
  2836. pagesToRemove.push(pageNum);
  2837. }
  2838. });
  2839. // 清理页面
  2840. pagesToRemove.forEach(function (pageNum) {
  2841. self.cleanupPage(pageNum);
  2842. });
  2843. }
  2844. },
  2845. // 设置分段加载配置
  2846. setProgressiveLoading: function (enabled, options) {
  2847. this.progressiveLoading = enabled;
  2848. if (options) {
  2849. if (options.chunkSize) this.chunkSize = options.chunkSize;
  2850. if (options.maxMemoryPages) this.maxMemoryPages = options.maxMemoryPages;
  2851. }
  2852. return this;
  2853. },
  2854. // 获取分段加载状态
  2855. getProgressiveLoadingStatus: function () {
  2856. return {
  2857. enabled: this.progressiveLoading,
  2858. chunkSize: this.chunkSize,
  2859. maxMemoryPages: this.maxMemoryPages,
  2860. loadedPages: this.loadedPages.size,
  2861. loadingQueue: this.loadingQueue.length,
  2862. memoryUsage: this.getMemoryUsage()
  2863. };
  2864. },
  2865. // 下载功能API
  2866. downloadPDF: function (filename) {
  2867. var self = this;
  2868. if (!self.thePDF) {
  2869. console.warn('No PDF loaded');
  2870. return Promise.reject('No PDF loaded');
  2871. }
  2872. // 如果没有提供文件名,尝试从PDF URL中提取文件名
  2873. if (!filename) {
  2874. if (self.options.pdfurl) {
  2875. // 从URL中提取文件名
  2876. var urlParts = self.options.pdfurl.split('/');
  2877. var urlFilename = urlParts[urlParts.length - 1];
  2878. // 如果URL中有查询参数,需要去掉
  2879. if (urlFilename.includes('?')) {
  2880. urlFilename = urlFilename.split('?')[0];
  2881. }
  2882. // 如果提取到的文件名有效,使用它;否则使用默认名称
  2883. if (urlFilename && urlFilename.includes('.pdf')) {
  2884. filename = urlFilename;
  2885. } else {
  2886. filename = 'document.pdf';
  2887. }
  2888. } else {
  2889. filename = 'document.pdf';
  2890. }
  2891. }
  2892. return self.getPDFWithAnnotations().then(function (pdfData) {
  2893. var blob = new Blob([pdfData], { type: 'application/pdf' });
  2894. var url = URL.createObjectURL(blob);
  2895. var a = document.createElement('a');
  2896. a.href = url;
  2897. a.download = filename;
  2898. document.body.appendChild(a);
  2899. a.click();
  2900. document.body.removeChild(a);
  2901. URL.revokeObjectURL(url);
  2902. self.trigger('download', { url: url, filename: filename });
  2903. return { url: url, filename: filename };
  2904. });
  2905. },
  2906. getPDFWithAnnotations: function () {
  2907. var self = this;
  2908. return new Promise(function (resolve, reject) {
  2909. if (!self.thePDF) {
  2910. reject('No PDF loaded');
  2911. return;
  2912. }
  2913. // 检查是否有图章或墨迹注释需要保存
  2914. var stampAnnotations = self.editorAnnotations.filter(function (ann) {
  2915. return ann.type === 'STAMP';
  2916. });
  2917. var inkAnnotations = self.editorAnnotations.filter(function (ann) {
  2918. return ann.type === 'INK';
  2919. });
  2920. if (stampAnnotations.length > 0 || inkAnnotations.length > 0) {
  2921. // 使用PDF.js官方的saveDocument方法
  2922. if (self.thePDF.saveDocument) {
  2923. try {
  2924. // 确保注释存储已修改
  2925. if (self.thePDF.annotationStorage) {
  2926. self.thePDF.annotationStorage.setModified(true);
  2927. }
  2928. self.thePDF.saveDocument().then(function (pdfData) {
  2929. resolve(pdfData);
  2930. }).catch(function (error) {
  2931. console.error('使用PDF.js saveDocument失败:', error.message);
  2932. // 如果saveDocument失败,回退到原始PDF
  2933. fetch(self.options.pdfurl)
  2934. .then(function (response) {
  2935. return response.arrayBuffer();
  2936. })
  2937. .then(function (data) {
  2938. console.warn('注意:返回的PDF不包含注释');
  2939. resolve(data);
  2940. })
  2941. .catch(reject);
  2942. });
  2943. } catch (error) {
  2944. console.error('保存PDF时发生错误:', error.message);
  2945. // 回退到原始PDF
  2946. if (self.options.pdfurl) {
  2947. fetch(self.options.pdfurl)
  2948. .then(function (response) {
  2949. return response.arrayBuffer();
  2950. })
  2951. .then(function (data) {
  2952. console.warn('注意:返回的PDF不包含注释');
  2953. resolve(data);
  2954. })
  2955. .catch(reject);
  2956. } else if (self.options.data) {
  2957. // 如果使用data配置,直接使用原始数据
  2958. console.warn('注意:返回的PDF不包含注释');
  2959. resolve(self.options.data);
  2960. } else {
  2961. reject('无法获取PDF数据');
  2962. }
  2963. }
  2964. } else {
  2965. // 如果没有saveDocument方法,回退到原始PDF
  2966. if (self.options.pdfurl) {
  2967. fetch(self.options.pdfurl)
  2968. .then(function (response) {
  2969. return response.arrayBuffer();
  2970. })
  2971. .then(function (data) {
  2972. console.warn('注意:返回的PDF不包含注释');
  2973. resolve(data);
  2974. })
  2975. .catch(reject);
  2976. } else if (self.options.data) {
  2977. // 如果使用data配置,直接使用原始数据
  2978. console.warn('注意:返回的PDF不包含注释');
  2979. resolve(self.options.data);
  2980. } else {
  2981. reject('无法获取PDF数据');
  2982. }
  2983. }
  2984. } else {
  2985. // 没有注释,直接返回原始PDF数据
  2986. if (self.options.pdfurl) {
  2987. fetch(self.options.pdfurl)
  2988. .then(function (response) {
  2989. return response.arrayBuffer();
  2990. })
  2991. .then(function (data) {
  2992. resolve(data);
  2993. })
  2994. .catch(reject);
  2995. } else if (self.options.data) {
  2996. // 如果使用data配置,直接使用原始数据
  2997. resolve(self.options.data);
  2998. } else {
  2999. reject('无法获取PDF数据');
  3000. }
  3001. }
  3002. });
  3003. },
  3004. // 使用PDF-lib库保存注释到PDF
  3005. savePDFWithAnnotations: async function (annotations) {
  3006. var self = this;
  3007. // 检查PDF-lib是否可用
  3008. if (typeof PDFLib === 'undefined') {
  3009. throw new Error('PDF-lib库未加载,请在HTML中引入:<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>');
  3010. }
  3011. // 1. 获取原始PDF数据
  3012. const response = await fetch(self.options.pdfurl);
  3013. const pdfData = await response.arrayBuffer();
  3014. // 2. 加载PDF文档
  3015. const pdfDoc = await PDFLib.PDFDocument.load(pdfData);
  3016. // 3. 遍历所有注释
  3017. for (const annotation of annotations) {
  3018. const page = pdfDoc.getPage(annotation.pageNum - 1);
  3019. const pageHeight = page.getHeight();
  3020. if (annotation.type === 'STAMP') {
  3021. // 处理图章注释
  3022. if (annotation.imageData) {
  3023. // 将base64图片转换为ArrayBuffer
  3024. const imageDataUrl = annotation.imageData;
  3025. const imageBytes = await fetch(imageDataUrl).then(r => r.arrayBuffer());
  3026. // 根据图片类型嵌入图片
  3027. let image;
  3028. if (imageDataUrl.startsWith('data:image/png')) {
  3029. image = await pdfDoc.embedPng(imageBytes);
  3030. } else if (imageDataUrl.startsWith('data:image/jpeg') || imageDataUrl.startsWith('data:image/jpg')) {
  3031. image = await pdfDoc.embedJpg(imageBytes);
  3032. } else {
  3033. console.warn('不支持的图片格式,跳过:', imageDataUrl.substring(0, 30));
  3034. continue;
  3035. }
  3036. // 计算PDF坐标(PDF坐标系原点在左下角)
  3037. const pdfY = pageHeight - annotation.y - annotation.height;
  3038. // 添加图章到页面
  3039. page.drawImage(image, {
  3040. x: annotation.x,
  3041. y: pdfY,
  3042. width: annotation.width,
  3043. height: annotation.height,
  3044. });
  3045. }
  3046. } else if (annotation.type === 'INK') {
  3047. // 处理墨迹注释
  3048. if (annotation.path && annotation.path.length > 1) {
  3049. // 构建SVG路径字符串
  3050. let pathData = `M ${annotation.path[0].x} ${pageHeight - annotation.path[0].y}`;
  3051. for (let i = 1; i < annotation.path.length; i++) {
  3052. const pdfY = pageHeight - annotation.path[i].y;
  3053. pathData += ` L ${annotation.path[i].x} ${pdfY}`;
  3054. }
  3055. // 解析颜色
  3056. const color = annotation.color || '#000000';
  3057. const r = parseInt(color.substring(1, 3), 16) / 255;
  3058. const g = parseInt(color.substring(3, 5), 16) / 255;
  3059. const b = parseInt(color.substring(5, 7), 16) / 255;
  3060. // 绘制墨迹路径
  3061. page.drawSvgPath(pathData, {
  3062. borderColor: PDFLib.rgb(r, g, b),
  3063. borderWidth: annotation.thickness || 1,
  3064. borderOpacity: annotation.opacity || 1
  3065. });
  3066. }
  3067. }
  3068. }
  3069. // 4. 保存PDF
  3070. const pdfBytes = await pdfDoc.save();
  3071. return pdfBytes;
  3072. },
  3073. // 获取当前状态
  3074. getStatus: function () {
  3075. var self = this;
  3076. return {
  3077. version: self.version,
  3078. totalPages: self.totalNum,
  3079. currentPage: self.currentNum,
  3080. scale: self.scale,
  3081. loadedPages: self.loadedCount
  3082. };
  3083. },
  3084. // 更新注释(当用户调整图章大小时)
  3085. updateAnnotation: function (annotationId, updates) {
  3086. var self = this;
  3087. // 更新前端显示
  3088. var annotation = self.editorAnnotations.find(function (ann) {
  3089. return ann.id === annotationId;
  3090. });
  3091. if (annotation) {
  3092. Object.assign(annotation, updates);
  3093. // 更新PDF.js的annotationStorage
  3094. if (self.thePDF && self.thePDF.annotationStorage) {
  3095. var pdfAnnotation = self.thePDF.annotationStorage.getRawValue(annotationId);
  3096. if (pdfAnnotation) {
  3097. // 更新PDF注释数据
  3098. Object.assign(pdfAnnotation, updates);
  3099. self.thePDF.annotationStorage.setValue(annotationId, pdfAnnotation);
  3100. }
  3101. }
  3102. // 重新渲染注释
  3103. self.renderAnnotation(annotation);
  3104. }
  3105. },
  3106. // 删除注释
  3107. removeAnnotation: function (annotationId) {
  3108. var self = this;
  3109. // 从PDF.js的annotationStorage中删除
  3110. if (self.thePDF && self.thePDF.annotationStorage) {
  3111. self.thePDF.annotationStorage.remove(annotationId);
  3112. }
  3113. // 从前端注释数组中删除
  3114. self.editorAnnotations = self.editorAnnotations.filter(function (ann) {
  3115. return ann.id !== annotationId;
  3116. });
  3117. // 从DOM中移除
  3118. var element = document.querySelector('[data-annotation-id="' + annotationId + '"]');
  3119. if (element) {
  3120. element.remove();
  3121. }
  3122. },
  3123. // 获取所有注释
  3124. getAllAnnotations: function () {
  3125. var self = this;
  3126. return self.editorAnnotations || [];
  3127. },
  3128. // 获取PDF注释存储状态
  3129. getAnnotationStorageStatus: function () {
  3130. var self = this;
  3131. if (self.thePDF && self.thePDF.annotationStorage) {
  3132. return {
  3133. size: self.thePDF.annotationStorage.size,
  3134. hasAnnotations: self.thePDF.annotationStorage.size > 0
  3135. };
  3136. }
  3137. return {
  3138. size: 0,
  3139. hasAnnotations: false
  3140. };
  3141. },
  3142. // 事件系统
  3143. // 编辑器功能API
  3144. setEditorMode: function (mode) {
  3145. var self = this;
  3146. // 验证模式
  3147. var validModes = ['NONE', 'FREETEXT', 'INK', 'STAMP'];
  3148. if (!validModes.includes(mode)) {
  3149. return false;
  3150. }
  3151. // 更新模式
  3152. self.annotationEditorMode = mode;
  3153. self.isEditing = (mode !== 'NONE');
  3154. // 触发事件
  3155. self.trigger('editorModeChanged', { mode: mode });
  3156. // 更新UI状态
  3157. self.updateEditorUI();
  3158. return true;
  3159. },
  3160. getEditorMode: function () {
  3161. return this.annotationEditorMode;
  3162. },
  3163. setEditorParam: function (param, value) {
  3164. var self = this;
  3165. // 初始化编辑器参数对象
  3166. if (!self.editorParams) {
  3167. self.editorParams = {};
  3168. }
  3169. // 设置参数
  3170. self.editorParams[param] = value;
  3171. self.trigger('editorParamChanged', { param: param, value: value });
  3172. return true;
  3173. },
  3174. getEditorParam: function (param) {
  3175. return this.editorParams[param] || null;
  3176. },
  3177. updateEditorUI: function () {
  3178. var self = this;
  3179. // 更新所有页面容器的编辑器状态
  3180. for (var i = 1; i <= self.totalNum; i++) {
  3181. var container = self.cache[i + ""].container;
  3182. if (container) {
  3183. // 移除之前的编辑器类
  3184. container.classList.remove('editor-freetext', 'editor-ink', 'editor-stamp');
  3185. // 添加新的编辑器类
  3186. if (self.isEditing) {
  3187. container.classList.add('editor-' + self.annotationEditorMode.toLowerCase());
  3188. }
  3189. // 更新鼠标样式
  3190. if (self.isEditing) {
  3191. container.style.cursor = self.getEditorCursor();
  3192. } else {
  3193. container.style.cursor = 'default';
  3194. }
  3195. }
  3196. }
  3197. },
  3198. getEditorCursor: function () {
  3199. switch (this.annotationEditorMode) {
  3200. case 'FREETEXT':
  3201. return 'text';
  3202. case 'INK':
  3203. return 'crosshair';
  3204. case 'STAMP':
  3205. return 'copy';
  3206. default:
  3207. return 'default';
  3208. }
  3209. },
  3210. // 添加注释
  3211. addAnnotation: function (annotation) {
  3212. var self = this;
  3213. // 生成唯一ID
  3214. annotation.id = 'annotation_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  3215. annotation.timestamp = new Date().toISOString();
  3216. // 添加到注释数组
  3217. self.editorAnnotations.push(annotation);
  3218. // 触发事件
  3219. self.trigger('annotationAdded', { annotation: annotation });
  3220. return annotation.id;
  3221. },
  3222. // 获取所有注释
  3223. getAnnotations: function () {
  3224. return this.editorAnnotations;
  3225. },
  3226. // 清除所有注释
  3227. clearAnnotations: function () {
  3228. this.editorAnnotations = [];
  3229. this.trigger('annotationsCleared');
  3230. },
  3231. // 初始化编辑器事件监听
  3232. initEditorEvents: function () {
  3233. var self = this;
  3234. // 监听页面点击事件
  3235. self.viewerContainer.addEventListener('click', function (e) {
  3236. if (!self.isEditing) return;
  3237. // 阻止事件冒泡,避免触发其他点击事件
  3238. e.stopPropagation();
  3239. var target = e.target;
  3240. var pageContainer = target.closest('.pageContainer');
  3241. if (pageContainer) {
  3242. var pageNum = parseInt(pageContainer.getAttribute('name').split('=')[1]);
  3243. self.handleEditorClick(e, pageNum);
  3244. }
  3245. });
  3246. // 墨迹功能已移除
  3247. // 监听鼠标按下事件
  3248. self.viewerContainer.addEventListener('mousedown', function (e) {
  3249. if (!self.isEditing) return;
  3250. var target = e.target;
  3251. var pageContainer = target.closest('.pageContainer');
  3252. if (pageContainer) {
  3253. self.handleEditorMouseDown(e, pageContainer);
  3254. }
  3255. });
  3256. // 监听鼠标释放事件
  3257. self.viewerContainer.addEventListener('mouseup', function (e) {
  3258. if (!self.isEditing) return;
  3259. var target = e.target;
  3260. var pageContainer = target.closest('.pageContainer');
  3261. if (pageContainer) {
  3262. self.handleEditorMouseUp(e, pageContainer);
  3263. }
  3264. });
  3265. },
  3266. // 处理编辑器点击
  3267. handleEditorClick: function (e, pageNum) {
  3268. var self = this;
  3269. // 检查是否在编辑模式
  3270. if (!self.isEditing || self.annotationEditorMode === 'NONE') {
  3271. return;
  3272. }
  3273. // 检查是否点击了现有注释
  3274. var existingAnnotation = self.findAnnotationAtPosition(e, pageNum);
  3275. if (existingAnnotation) {
  3276. self.selectAnnotation(existingAnnotation);
  3277. return;
  3278. }
  3279. // 注释功能已移除
  3280. },
  3281. // 渲染注释到页面
  3282. renderAnnotation: function (annotation) {
  3283. var self = this;
  3284. // 获取页面容器
  3285. var pageContainer = self.cache[annotation.pageNum + ""].container;
  3286. if (!pageContainer) {
  3287. return;
  3288. }
  3289. // 创建图章显示元素
  3290. var stampElement = document.createElement('div');
  3291. stampElement.className = 'stamp-annotation-display';
  3292. stampElement.style.position = 'absolute';
  3293. stampElement.style.left = annotation.x + 'px';
  3294. stampElement.style.top = annotation.y + 'px';
  3295. stampElement.style.width = annotation.width + 'px';
  3296. stampElement.style.height = annotation.height + 'px';
  3297. stampElement.style.border = '2px solid #007bff';
  3298. stampElement.style.cursor = 'move';
  3299. stampElement.style.zIndex = '1000';
  3300. stampElement.setAttribute('data-annotation-id', annotation.id);
  3301. // 创建图片元素
  3302. var imgElement = document.createElement('img');
  3303. imgElement.src = annotation.image;
  3304. imgElement.style.width = '100%';
  3305. imgElement.style.height = '100%';
  3306. imgElement.style.objectFit = 'contain';
  3307. imgElement.draggable = false;
  3308. stampElement.appendChild(imgElement);
  3309. pageContainer.appendChild(stampElement);
  3310. // 添加点击事件监听器
  3311. stampElement.addEventListener('click', function (e) {
  3312. e.stopPropagation();
  3313. self.selectAnnotation(annotation);
  3314. });
  3315. },
  3316. // 选择注释
  3317. selectAnnotation: function (annotation) {
  3318. var self = this;
  3319. // 移除之前选中的注释
  3320. var existingSelected = document.querySelector('.stamp-annotation-display.selected');
  3321. if (existingSelected) {
  3322. existingSelected.classList.remove('selected');
  3323. }
  3324. // 选中当前注释
  3325. var stampElement = document.querySelector('[data-annotation-id="' + annotation.id + '"]');
  3326. if (stampElement) {
  3327. stampElement.classList.add('selected');
  3328. stampElement.style.border = '2px solid #ff0000';
  3329. // 创建控制点
  3330. self.createResizeHandles(stampElement, annotation);
  3331. }
  3332. },
  3333. // 创建调整大小的控制点
  3334. createResizeHandles: function (element, annotation) {
  3335. var self = this;
  3336. // 移除之前的控制点
  3337. var existingHandles = element.querySelectorAll('.resize-handle');
  3338. existingHandles.forEach(function (handle) {
  3339. handle.remove();
  3340. });
  3341. // 创建控制点
  3342. var handles = ['nw', 'ne', 'sw', 'se', 'n', 's', 'e', 'w'];
  3343. handles.forEach(function (handle) {
  3344. var handleElement = document.createElement('div');
  3345. handleElement.className = 'resize-handle resize-handle-' + handle;
  3346. handleElement.style.position = 'absolute';
  3347. handleElement.style.width = '8px';
  3348. handleElement.style.height = '8px';
  3349. handleElement.style.backgroundColor = '#007bff';
  3350. handleElement.style.border = '1px solid #fff';
  3351. handleElement.style.cursor = self.getResizeCursor(handle);
  3352. handleElement.style.zIndex = '1001';
  3353. // 设置位置
  3354. var position = self.getHandlePosition(handle, element.offsetWidth, element.offsetHeight);
  3355. handleElement.style.left = position.x + 'px';
  3356. handleElement.style.top = position.y + 'px';
  3357. element.appendChild(handleElement);
  3358. });
  3359. },
  3360. // 获取调整大小的光标
  3361. getResizeCursor: function (handle) {
  3362. var cursors = {
  3363. 'nw': 'nw-resize',
  3364. 'ne': 'ne-resize',
  3365. 'sw': 'sw-resize',
  3366. 'se': 'se-resize',
  3367. 'n': 'n-resize',
  3368. 's': 's-resize',
  3369. 'e': 'e-resize',
  3370. 'w': 'w-resize'
  3371. };
  3372. return cursors[handle] || 'default';
  3373. },
  3374. // 获取控制点位置
  3375. getHandlePosition: function (handle, width, height) {
  3376. var positions = {
  3377. 'nw': { x: -4, y: -4 },
  3378. 'ne': { x: width - 4, y: -4 },
  3379. 'sw': { x: -4, y: height - 4 },
  3380. 'se': { x: width - 4, y: height - 4 },
  3381. 'n': { x: width / 2 - 4, y: -4 },
  3382. 's': { x: width / 2 - 4, y: height - 4 },
  3383. 'e': { x: width - 4, y: height / 2 - 4 },
  3384. 'w': { x: -4, y: height / 2 - 4 }
  3385. };
  3386. return positions[handle] || { x: 0, y: 0 };
  3387. },
  3388. // 处理编辑器鼠标按下
  3389. handleEditorMouseDown: function (e, pageContainer) {
  3390. var self = this;
  3391. // 墨迹功能已移除
  3392. },
  3393. // 处理编辑器鼠标释放
  3394. handleEditorMouseUp: async function (e, pageContainer) {
  3395. var self = this;
  3396. // 墨迹功能已移除
  3397. },
  3398. // 查找指定位置的注释
  3399. findAnnotationAtPosition: function (e, pageNum) {
  3400. var self = this;
  3401. var rect = e.target.getBoundingClientRect();
  3402. var x = e.clientX - rect.left;
  3403. var y = e.clientY - rect.top;
  3404. // 查找该页面的所有注释
  3405. for (var i = 0; i < self.editorAnnotations.length; i++) {
  3406. var annotation = self.editorAnnotations[i];
  3407. if (annotation.pageNum === pageNum) {
  3408. // 检查点击位置是否在注释范围内
  3409. if (self.isPointInAnnotation(x, y, annotation)) {
  3410. return annotation;
  3411. }
  3412. }
  3413. }
  3414. return null;
  3415. },
  3416. // 检查点是否在注释范围内
  3417. isPointInAnnotation: function (x, y, annotation) {
  3418. switch (annotation.type) {
  3419. case 'FREETEXT':
  3420. // 文本注释:检查是否在文本区域内
  3421. return x >= annotation.x && x <= annotation.x + annotation.width &&
  3422. y >= annotation.y && y <= annotation.y + annotation.height;
  3423. case 'STAMP':
  3424. // 图章注释:检查是否在图片区域内
  3425. return x >= annotation.x && x <= annotation.x + annotation.width &&
  3426. y >= annotation.y && y <= annotation.y + annotation.height;
  3427. case 'INK':
  3428. // 墨迹注释:检查是否在路径区域内
  3429. return this.isPointInInkPath(x, y, annotation);
  3430. default:
  3431. return false;
  3432. }
  3433. },
  3434. // 检查点是否在墨迹路径内
  3435. isPointInInkPath: function (x, y, annotation) {
  3436. // 简化的墨迹路径检测
  3437. // 实际实现中可以使用更复杂的路径算法
  3438. if (!annotation.path || annotation.path.length === 0) return false;
  3439. var tolerance = 10; // 容差范围
  3440. for (var i = 0; i < annotation.path.length; i++) {
  3441. var point = annotation.path[i];
  3442. var distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
  3443. if (distance <= tolerance) {
  3444. return true;
  3445. }
  3446. }
  3447. return false;
  3448. },
  3449. // 选择注释
  3450. selectAnnotation: function (annotation) {
  3451. var self = this;
  3452. // 取消之前的选择
  3453. self.deselectAllAnnotations();
  3454. // 设置当前选择的注释
  3455. self.selectedAnnotation = annotation;
  3456. // 触发选择事件
  3457. self.trigger('annotationSelected', { annotation: annotation });
  3458. return annotation;
  3459. },
  3460. // 取消选择所有注释
  3461. deselectAllAnnotations: function () {
  3462. var self = this;
  3463. if (self.selectedAnnotation) {
  3464. var prevAnnotation = self.selectedAnnotation;
  3465. self.selectedAnnotation = null;
  3466. // 触发取消选择事件
  3467. self.trigger('annotationDeselected', { annotation: prevAnnotation });
  3468. }
  3469. },
  3470. // 获取当前选择的注释
  3471. getSelectedAnnotation: function () {
  3472. return this.selectedAnnotation;
  3473. },
  3474. // 移动注释
  3475. moveAnnotation: function (annotationId, newX, newY) {
  3476. var self = this;
  3477. var index = self.editorAnnotations.findIndex(function (ann) {
  3478. return ann.id === annotationId;
  3479. });
  3480. if (index !== -1) {
  3481. var annotation = self.editorAnnotations[index];
  3482. var oldX = annotation.x;
  3483. var oldY = annotation.y;
  3484. // 更新位置
  3485. annotation.x = newX;
  3486. annotation.y = newY;
  3487. // 触发移动事件
  3488. self.trigger('annotationMoved', {
  3489. annotation: annotation,
  3490. oldPosition: { x: oldX, y: oldY },
  3491. newPosition: { x: newX, y: newY }
  3492. });
  3493. return true;
  3494. }
  3495. return false;
  3496. },
  3497. // 调整注释大小
  3498. resizeAnnotation: function (annotationId, newWidth, newHeight) {
  3499. var self = this;
  3500. var index = self.editorAnnotations.findIndex(function (ann) {
  3501. return ann.id === annotationId;
  3502. });
  3503. if (index !== -1) {
  3504. var annotation = self.editorAnnotations[index];
  3505. var oldWidth = annotation.width;
  3506. var oldHeight = annotation.height;
  3507. // 更新尺寸
  3508. annotation.width = newWidth;
  3509. annotation.height = newHeight;
  3510. // 触发调整大小事件
  3511. self.trigger('annotationResized', {
  3512. annotation: annotation,
  3513. oldSize: { width: oldWidth, height: oldHeight },
  3514. newSize: { width: newWidth, height: newHeight }
  3515. });
  3516. return true;
  3517. }
  3518. return false;
  3519. },
  3520. // 获取指定页面的所有注释
  3521. getAnnotationsByPage: function (pageNum) {
  3522. return this.editorAnnotations.filter(function (annotation) {
  3523. return annotation.pageNum === pageNum;
  3524. });
  3525. },
  3526. // 获取指定类型的注释
  3527. getAnnotationsByType: function (type) {
  3528. return this.editorAnnotations.filter(function (annotation) {
  3529. return annotation.type === type;
  3530. });
  3531. },
  3532. // 导出注释数据
  3533. exportAnnotations: function () {
  3534. return {
  3535. annotations: this.editorAnnotations.slice(),
  3536. exportTime: new Date().toISOString(),
  3537. version: this.version
  3538. };
  3539. },
  3540. // 导入注释数据
  3541. importAnnotations: function (data) {
  3542. var self = this;
  3543. if (data && data.annotations && Array.isArray(data.annotations)) {
  3544. // 清除现有注释
  3545. self.clearAnnotations();
  3546. // 导入新注释
  3547. self.editorAnnotations = data.annotations.slice();
  3548. // 触发导入事件
  3549. self.trigger('annotationsImported', {
  3550. count: self.editorAnnotations.length,
  3551. importTime: data.exportTime
  3552. });
  3553. return true;
  3554. }
  3555. return false;
  3556. },
  3557. // 初始化沙箱管理器
  3558. initSandbox: function () {
  3559. var self = this;
  3560. if (!self.sandboxEnabled || !self.pdfjsLib || !self.pdfjsLib.SandboxManager) {
  3561. return;
  3562. }
  3563. try {
  3564. // 创建沙箱管理器实例
  3565. self.sandboxManager = new self.pdfjsLib.SandboxManager({
  3566. // 沙箱配置
  3567. allowScripts: false, // 禁止JavaScript执行
  3568. allowForms: true, // 允许表单交互
  3569. allowPopups: false, // 禁止弹窗
  3570. allowSameOrigin: true, // 允许同源访问
  3571. // 安全策略
  3572. sandbox: 'allow-same-origin allow-scripts',
  3573. referrerPolicy: 'strict-origin-when-cross-origin',
  3574. // 容器配置
  3575. container: self.viewer,
  3576. // 事件处理
  3577. onError: function (error) {
  3578. console.warn('Sandbox error:', error);
  3579. },
  3580. onSecurityViolation: function (violation) {
  3581. console.warn('Security violation blocked:', violation);
  3582. }
  3583. });
  3584. console.info('PDF.js sandbox initialized successfully');
  3585. } catch (error) {
  3586. console.warn('Failed to initialize sandbox:', error);
  3587. self.sandboxEnabled = false;
  3588. }
  3589. },
  3590. // 启用/禁用沙箱
  3591. setSandboxEnabled: function (enabled) {
  3592. this.sandboxEnabled = enabled;
  3593. if (enabled && !this.sandboxManager) {
  3594. this.initSandbox();
  3595. } else if (!enabled && this.sandboxManager) {
  3596. this.destroySandbox();
  3597. }
  3598. },
  3599. // 销毁沙箱
  3600. destroySandbox: function () {
  3601. if (this.sandboxManager) {
  3602. try {
  3603. this.sandboxManager.destroy();
  3604. this.sandboxManager = null;
  3605. } catch (error) {
  3606. console.warn('Error destroying sandbox:', error);
  3607. }
  3608. }
  3609. },
  3610. // 获取沙箱状态
  3611. getSandboxStatus: function () {
  3612. return {
  3613. enabled: this.sandboxEnabled,
  3614. initialized: !!this.sandboxManager,
  3615. active: this.sandboxManager ? this.sandboxManager.isActive() : false
  3616. };
  3617. },
  3618. // 处理密码错误
  3619. handlePasswordError: function (error) {
  3620. var self = this;
  3621. // 如果正在验证密码,显示错误信息
  3622. if (self.passwordValidating) {
  3623. self.showPasswordError('密码错误,请重新输入');
  3624. self.passwordValidating = false; // 重置标志
  3625. } else if (self.passwordPrompt && self.passwordPrompt.parentNode) {
  3626. // 如果密码框已经存在,显示错误信息
  3627. self.showPasswordError('密码错误,请重新输入');
  3628. } else {
  3629. // 如果没有密码框,显示密码输入框
  3630. self.showPasswordPrompt();
  3631. }
  3632. },
  3633. // 显示密码输入框
  3634. showPasswordPrompt: function () {
  3635. var self = this;
  3636. // 如果已有密码框,先移除
  3637. if (self.passwordPrompt) {
  3638. self.hidePasswordPrompt();
  3639. }
  3640. // 使用原生DOM创建方式,避免innerHTML解析问题
  3641. self.passwordPrompt = document.createElement('div');
  3642. self.passwordPrompt.className = 'pdfh5-password-prompt';
  3643. self.passwordPrompt.style.cssText = `
  3644. position: fixed;
  3645. top: 0;
  3646. left: 0;
  3647. width: 100%;
  3648. height: 100%;
  3649. background: rgba(0,0,0,0.7);
  3650. z-index: 9999;
  3651. display: flex;
  3652. align-items: center;
  3653. justify-content: center;
  3654. padding: 16px;
  3655. box-sizing: border-box;
  3656. `;
  3657. // 创建内容容器
  3658. var contentDiv = document.createElement('div');
  3659. contentDiv.style.cssText = `
  3660. background: white;
  3661. padding: 24px;
  3662. border-radius: 12px;
  3663. box-shadow: 0 8px 32px rgba(0,0,0,0.12);
  3664. width: 100%;
  3665. max-width: 400px;
  3666. text-align: center;
  3667. box-sizing: border-box;
  3668. `;
  3669. // 移动端适配
  3670. if (window.innerWidth <= 768) {
  3671. contentDiv.style.cssText += `
  3672. padding: 20px;
  3673. margin: 0 8px;
  3674. border-radius: 8px;
  3675. `;
  3676. }
  3677. // 创建标题
  3678. var title = document.createElement('div');
  3679. title.textContent = '提示';
  3680. title.style.cssText = 'margin: 0 0 16px 0; color: #333; font-size: 18px; font-weight: 600;';
  3681. contentDiv.appendChild(title);
  3682. // 创建错误提示信息(初始隐藏)
  3683. var errorMessage = document.createElement('div');
  3684. errorMessage.id = 'pdfh5-password-error';
  3685. errorMessage.textContent = '密码错误,请重新输入';
  3686. errorMessage.style.cssText = `
  3687. margin: 0 0 16px 0;
  3688. color: #dc3545;
  3689. font-size: 14px;
  3690. display: none;
  3691. text-align: center;
  3692. `;
  3693. contentDiv.appendChild(errorMessage);
  3694. // 创建密码输入框
  3695. var passwordInput = document.createElement('input');
  3696. passwordInput.type = 'password';
  3697. passwordInput.id = 'pdfh5-password-input';
  3698. passwordInput.placeholder = '请输入PDF文件的密码';
  3699. passwordInput.style.cssText = `
  3700. width: 100%;
  3701. padding: 12px 16px;
  3702. border: 2px solid #e1e5e9;
  3703. border-radius: 8px;
  3704. margin-bottom: 24px;
  3705. font-size: 16px;
  3706. box-sizing: border-box;
  3707. outline: none;
  3708. transition: border-color 0.3s ease;
  3709. `;
  3710. // 移动端输入框适配
  3711. if (window.innerWidth <= 768) {
  3712. passwordInput.style.cssText += `
  3713. padding: 14px 16px;
  3714. font-size: 16px;
  3715. margin-bottom: 20px;
  3716. `;
  3717. }
  3718. contentDiv.appendChild(passwordInput);
  3719. // 创建按钮容器
  3720. var buttonContainer = document.createElement('div');
  3721. buttonContainer.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;';
  3722. // 移动端按钮布局适配
  3723. if (window.innerWidth <= 768) {
  3724. buttonContainer.style.cssText = 'display: flex; gap: 8px; justify-content: center; flex-direction: column;';
  3725. }
  3726. // 创建确定按钮
  3727. var submitBtn = document.createElement('button');
  3728. submitBtn.id = 'pdfh5-password-submit';
  3729. submitBtn.textContent = '确定';
  3730. submitBtn.style.cssText = `
  3731. background: #007bff;
  3732. color: white;
  3733. border: none;
  3734. padding: 12px 24px;
  3735. border-radius: 8px;
  3736. cursor: pointer;
  3737. font-size: 14px;
  3738. font-weight: 500;
  3739. transition: all 0.2s ease;
  3740. min-width: 80px;
  3741. `;
  3742. // 移动端按钮适配
  3743. if (window.innerWidth <= 768) {
  3744. submitBtn.style.cssText += `
  3745. padding: 14px 24px;
  3746. font-size: 16px;
  3747. width: 100%;
  3748. min-width: auto;
  3749. `;
  3750. }
  3751. buttonContainer.appendChild(submitBtn);
  3752. contentDiv.appendChild(buttonContainer);
  3753. self.passwordPrompt.appendChild(contentDiv);
  3754. document.body.appendChild(self.passwordPrompt);
  3755. // 确保DOM元素已添加到页面
  3756. if (!self.passwordPrompt.parentNode) {
  3757. console.error('Failed to add password prompt to DOM');
  3758. return;
  3759. }
  3760. // 绑定事件 - 使用setTimeout确保DOM完全渲染
  3761. setTimeout(function () {
  3762. // 重新获取元素引用,确保它们存在
  3763. var passwordInputEl = document.getElementById('pdfh5-password-input');
  3764. var submitBtnEl = document.getElementById('pdfh5-password-submit');
  3765. if (!passwordInputEl || !submitBtnEl) {
  3766. console.error('Password prompt elements not found after creation');
  3767. return;
  3768. }
  3769. // 自动聚焦
  3770. passwordInputEl.focus();
  3771. // 输入框焦点效果
  3772. passwordInputEl.addEventListener('focus', function () {
  3773. this.style.borderColor = '#007bff';
  3774. });
  3775. passwordInputEl.addEventListener('blur', function () {
  3776. this.style.borderColor = '#e1e5e9';
  3777. });
  3778. // 按钮悬停效果
  3779. submitBtnEl.addEventListener('mouseenter', function () {
  3780. this.style.background = '#0056b3';
  3781. this.style.transform = 'translateY(-1px)';
  3782. });
  3783. submitBtnEl.addEventListener('mouseleave', function () {
  3784. this.style.background = '#007bff';
  3785. this.style.transform = 'translateY(0)';
  3786. });
  3787. // 回车提交
  3788. passwordInputEl.addEventListener('keypress', function (e) {
  3789. if (e.key === 'Enter') {
  3790. self.submitPassword();
  3791. }
  3792. });
  3793. // 提交按钮
  3794. submitBtnEl.addEventListener('click', function () {
  3795. self.submitPassword();
  3796. });
  3797. // 监听窗口大小变化,重新适配弹窗
  3798. var resizeHandler = function () {
  3799. if (self.passwordPrompt && self.passwordPrompt.parentNode) {
  3800. // 重新应用移动端样式
  3801. var isMobile = window.innerWidth <= 768;
  3802. var contentDiv = self.passwordPrompt.querySelector('div');
  3803. var title = contentDiv.querySelector('h3');
  3804. var passwordInput = document.getElementById('pdfh5-password-input');
  3805. var buttonContainer = contentDiv.querySelector('div:last-child');
  3806. var submitBtn = document.getElementById('pdfh5-password-submit');
  3807. if (contentDiv) {
  3808. if (isMobile) {
  3809. contentDiv.style.cssText = `
  3810. background: white;
  3811. padding: 20px;
  3812. border-radius: 8px;
  3813. box-shadow: 0 8px 32px rgba(0,0,0,0.12);
  3814. width: 100%;
  3815. max-width: 400px;
  3816. text-align: center;
  3817. box-sizing: border-box;
  3818. margin: 0 8px;
  3819. `;
  3820. } else {
  3821. contentDiv.style.cssText = `
  3822. background: white;
  3823. padding: 24px;
  3824. border-radius: 12px;
  3825. box-shadow: 0 8px 32px rgba(0,0,0,0.12);
  3826. width: 100%;
  3827. max-width: 400px;
  3828. text-align: center;
  3829. box-sizing: border-box;
  3830. `;
  3831. }
  3832. }
  3833. if (title) {
  3834. if (isMobile) {
  3835. title.style.cssText = 'margin: 0 0 12px 0; color: #333; font-size: 16px; font-weight: 600;';
  3836. } else {
  3837. title.style.cssText = 'margin: 0 0 16px 0; color: #333; font-size: 18px; font-weight: 600;';
  3838. }
  3839. }
  3840. if (passwordInput) {
  3841. if (isMobile) {
  3842. passwordInput.style.cssText = `
  3843. width: 100%;
  3844. padding: 14px 16px;
  3845. border: 2px solid #e1e5e9;
  3846. border-radius: 8px;
  3847. margin-bottom: 20px;
  3848. font-size: 16px;
  3849. box-sizing: border-box;
  3850. outline: none;
  3851. transition: border-color 0.3s ease;
  3852. `;
  3853. } else {
  3854. passwordInput.style.cssText = `
  3855. width: 100%;
  3856. padding: 12px 16px;
  3857. border: 2px solid #e1e5e9;
  3858. border-radius: 8px;
  3859. margin-bottom: 24px;
  3860. font-size: 16px;
  3861. box-sizing: border-box;
  3862. outline: none;
  3863. transition: border-color 0.3s ease;
  3864. `;
  3865. }
  3866. }
  3867. if (buttonContainer) {
  3868. if (isMobile) {
  3869. buttonContainer.style.cssText = 'display: flex; gap: 8px; justify-content: flex-end; flex-direction: column;';
  3870. } else {
  3871. buttonContainer.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;';
  3872. }
  3873. }
  3874. if (submitBtn) {
  3875. if (isMobile) {
  3876. submitBtn.style.cssText = `
  3877. background: #007bff;
  3878. color: white;
  3879. border: none;
  3880. padding: 14px 24px;
  3881. border-radius: 8px;
  3882. cursor: pointer;
  3883. font-size: 16px;
  3884. font-weight: 500;
  3885. transition: all 0.2s ease;
  3886. width: 100%;
  3887. `;
  3888. } else {
  3889. submitBtn.style.cssText = `
  3890. background: #007bff;
  3891. color: white;
  3892. border: none;
  3893. padding: 12px 24px;
  3894. border-radius: 8px;
  3895. cursor: pointer;
  3896. font-size: 14px;
  3897. font-weight: 500;
  3898. transition: all 0.2s ease;
  3899. min-width: 80px;
  3900. `;
  3901. }
  3902. }
  3903. }
  3904. };
  3905. // 添加窗口大小变化监听
  3906. window.addEventListener('resize', resizeHandler);
  3907. // 在密码框关闭时移除监听器
  3908. var originalHidePasswordPrompt = self.hidePasswordPrompt;
  3909. self.hidePasswordPrompt = function () {
  3910. window.removeEventListener('resize', resizeHandler);
  3911. originalHidePasswordPrompt.call(self);
  3912. };
  3913. }, 100);
  3914. },
  3915. // 提交密码
  3916. submitPassword: function () {
  3917. var self = this;
  3918. var passwordInput = document.getElementById('pdfh5-password-input');
  3919. if (!passwordInput) {
  3920. console.error('Password input not found');
  3921. return;
  3922. }
  3923. var password = passwordInput.value.trim();
  3924. if (!password) {
  3925. self.showPasswordError('请输入密码');
  3926. return;
  3927. }
  3928. // 隐藏错误信息
  3929. self.hidePasswordError();
  3930. // 设置一个标志,表示正在验证密码
  3931. self.passwordValidating = true;
  3932. // 重新加载PDF,使用新密码
  3933. self.loadPDFWithPassword(password);
  3934. },
  3935. // 显示密码错误信息
  3936. showPasswordError: function (message) {
  3937. var self = this;
  3938. var errorElement = document.getElementById('pdfh5-password-error');
  3939. if (errorElement) {
  3940. errorElement.textContent = message || '密码错误,请重新输入';
  3941. errorElement.style.display = 'block';
  3942. // 清空密码输入框
  3943. var passwordInput = document.getElementById('pdfh5-password-input');
  3944. if (passwordInput) {
  3945. passwordInput.value = '';
  3946. passwordInput.focus();
  3947. }
  3948. }
  3949. },
  3950. // 隐藏密码错误信息
  3951. hidePasswordError: function () {
  3952. var self = this;
  3953. var errorElement = document.getElementById('pdfh5-password-error');
  3954. if (errorElement) {
  3955. errorElement.style.display = 'none';
  3956. }
  3957. },
  3958. // 隐藏密码输入框
  3959. hidePasswordPrompt: function () {
  3960. var self = this;
  3961. if (self.passwordPrompt && self.passwordPrompt.parentNode) {
  3962. self.passwordPrompt.parentNode.removeChild(self.passwordPrompt);
  3963. self.passwordPrompt = null;
  3964. }
  3965. },
  3966. // 使用密码重新加载PDF
  3967. loadPDFWithPassword: function (password) {
  3968. var self = this;
  3969. // 更新选项中的密码
  3970. self.options.password = password;
  3971. // 重新加载PDF
  3972. self.loadPDF();
  3973. },
  3974. // 获取密码配置状态
  3975. getPasswordConfig: function () {
  3976. return {
  3977. hasPassword: !!this.options.password
  3978. };
  3979. },
  3980. destroy: function (callback) {
  3981. // 销毁沙箱
  3982. this.destroySandbox();
  3983. // 清理密码相关资源
  3984. this.hidePasswordPrompt();
  3985. // 销毁PinchZoom
  3986. if (this.pinchZoom) {
  3987. this.pinchZoom.destroy();
  3988. this.pinchZoom = null;
  3989. }
  3990. // 清理窗口大小变化监听器
  3991. if (this.resizeHandler) {
  3992. window.removeEventListener('resize', this.resizeHandler);
  3993. this.resizeHandler = null;
  3994. }
  3995. if (this.thePDF && this.thePDF.destroy) {
  3996. this.thePDF.destroy();
  3997. this.thePDF = null;
  3998. }
  3999. if (this.viewerContainer) {
  4000. this.viewerContainer.parentNode.removeChild(this.viewerContainer);
  4001. this.viewerContainer = null;
  4002. }
  4003. if (this.container) {
  4004. this.container.innerHTML = "";
  4005. }
  4006. callback && callback.call(this);
  4007. }
  4008. };
  4009. return Pdfh5;
  4010. });