HotPDF Component kan söka och ersätta text inuti en befintlig PDF från Delphi och C++Builder. SearchLoadedPageText och SearchLoadedDocumentText lokaliserar varje förekomst av en sträng med precision på glyfnivå, och ReplaceLoadedPageText och ReplaceLoadedDocumentText skriver om de matchade byten på plats — förutsatt att varje ersättningstecken kan omkodas via det ursprungliga typsnittet, en fysisk begränsning som den här artikeln behandlar ärligt snarare än att gömma i en fotnot
Begäran bakom denna funktion är alltid vardaglig. Ett företag byter namn och tre tusen arkiverade fakturor bär fortfarande det gamla namnet. En avtalsmall levererades med förra årets utgångsdatum. En produktkod togs ur bruk och varje datablad som nämner den behöver istället den efterföljande koden. I en ordbehandlare är var och en av dessa ett trettio sekunders jobb. I a PDF är det ett genuint svårt problem, och att förstå varför gör skillnaden mellan att använda API:et väl och att skicka in en felrapport som i själva verket är ett citat ur specifikationen
Varför är det så svårt att ersätta text i en PDF?
Att ersätta text i en PDF är svårt eftersom en PDF-sida inte innehåller redigerbar text — den innehåller positionerade glyfer. Under textvisningsmodellen i ISO 32000-1 §9.4 driver en innehållsström operatorer som Tj och TJ som ritar sekvenser av teckenkoder vid koordinater som fastställts av textmatrisen. Dessa koder är inte Unicode; de är index till den kodning som sidans typsnitt deklarerar, och mappningen tillbaka till läsbara tecken kan finnas i en /ToUnicode CMap, en skillnadsmatris för kodning eller en CID-mappningskedja. Det finns inget styckeobjekt, inget textflöde och ingen garanti för att ett visuellt ord ens lagras som en enda sträng
Ersättning lägger till ett andra svårighetslager ovanpå avkodningen: du måste veta exakt vilka byte i den ursprungliga strömmen som producerade varje glyf, så att du kan skarva in nya byte i just det intervallet och ingenting annat. En textextraherare har råd att kasta bort bytepositionerna när den väl har fått ut Unicode. Det kan inte en ersättare. Det är därför HotPDF delade upp arbetet över två utgåvor — v2.251.0 byggde förskjutningsspårnings- och söklaveret, och v2.252.0 byggde omskrivningslagret ovanpå det
Hitta text: sökning på glyfnivå med byte-förskjutningsspårning
HotPDF:s SearchLoadedDocumentText hittar varje förekomst av ett sökord genom att matcha mot den avkodade Unicode-glyfsekvensen för varje sida, inte mot råa strömbyte, så en träff är en träff oavsett hur typsnittet kodade den. Infrastrukturen under introducerades i v2.251.0: tolkaren för innehållsströmmen registrerar ett StartOfs/EndOfs-byteintervall för varje strängoperand — inklusive dess ( )- eller < >-avgränsare — och varje avkodad glyf bär med sig en TokenIndex/ItemIndex/ByteOffset-trippel som pekar tillbaka till exakt den operand, det TJ-array-objekt och den kodenhet som producerade den. Samma glyftolkare driver extraherings-API:et som beskrivs i extrahering av text från en inläst PDF i Delphi; sökningen behåller helt enkelt det ursprung som extraheringen kastar bort
Varje träff returneras som en THPDFTextMatch-post som bär med sig sidindex, det inkluderande glyfintervallet, användarrymdens X/Y-ursprung och bredd för träffen, källtoken och objektindex samt själva den matchade texten. Det räcker för att driva en markeringsöverlagring, ett granskningsgränssnitt eller ersättningssteget. En sökning som inte hittar något returnerar en tom array istället för att misslyckas, så anropsmönstret förblir enkelt
var
Pdf: THotPDF;
Matches: THPDFTextMatchArray;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('invoices-2025.pdf') > 0 then
begin
if Pdf.SearchLoadedDocumentText('Acme Corp', False, Matches) then
for I := 0 to Length(Matches) - 1 do
WriteLn(Format('sida %d at (%.1f, %.1f): "%s"',
[Matches[I].PageIndex, Matches[I].X, Matches[I].Y,
Matches[I].Text]));
end;
finally
Pdf.Free;
end;
end;
Ett medvetet designval förtjänar en anmärkning. När CaseSensitive är False fälls skiftläget endast för ASCII-tecken, avsiktligt: fullständig Unicode-skiftlägesfällning fungerar olika i verktygskedjorna från Delphi 5 till XE som HotPDF stöder, och ett sök-API som hittar olika träffar beroende på vilken kompilator som byggde din applikation är sämre än ett med en dokumenterad, förutsägbar gräns. För latinsk affärstext — namn, koder, datum — ASCII-fällning täcker de praktiska fallen
Ersätta text: omvänd kodning och kirurgisk skarvning
ReplaceLoadedDocumentText, som lades till i HotPDF v2.252.0, skriver om varje förekomst av ett sökord genom att köra avkodningsmaskineriet baklänges. Funktionen HPDFEncodeUnicode är motsatsen till teckenkodsavkodaren: den går igenom samma strategikedja i omvänd ordning — uppslagning av /ToUnicode bfchar och bfrange, CID-mappning av kodningsströmmen, Type0-identitetsmappningar och de fördefinierade WinAnsi- och MacRoman-tabellerna — för att omvandla varje ersättningstecken tillbaka till de teckenkodsbyte som det ursprungliga typsnittet förväntar sig. De omkodade byten serialiseras sedan till en välformad strängliteral eller hexadecimal sträng, vilket speglar tolkarens egna eskaperingsregler så att en tolknings- och omserialiseringscykel är stabil
Själva skarvningen är kirurgisk snarare än storskalig. Endast det kodbyteintervall som täcks av träffen ersätts inuti strängoperanden; icke-matchade byte i samma operand, tomrummet mellan symboler och varje omgivande operator bevaras ordagrant, byte för byte. Att ersätta bca inuti abcabc ger a + ersättning + bc, inte en förstörd operand. Ersättningar kan vara kortare eller längre än sökordet — literalen om-serialiseras och strömmens /Length uppdateras — och varje /Contents-ström på en sida med flera strömmar bearbetas isolerat så att sidan förblir välformad
var
Pdf: THotPDF;
ReplaceCount: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('contract-draft.pdf') > 0 then
begin
if Pdf.ReplaceLoadedDocumentText('2025-12-31', '2026-12-31',
True, ReplaceCount) then
WriteLn(Format('%d operand rewrites performed', [ReplaceCount]));
Pdf.SaveLoadedDocument('contract-final.pdf');
end;
finally
Pdf.Free;
end;
end;
Observera vad API:et inte gör: det typograferar inte om sidan. PDF har inget omflöde (reflow), så en ersättning som är visuellt bredare än originalet kommer helt enkelt att ta upp mer horisontellt utrymme och kan tränga undan det som ritades till höger om den. Ersättningar med samma eller liknande längd — datum, versionssträngar, artikelnummer, namnkorrigeringar — är den ideala användningen. Storskaliga omformuleringar hör hemma i källdokumentet, inte i PDF-filen
Varför kan du inte ersätta text med tecken som typsnittsdelmängden aldrig innehöll?
Du kan inte ersätta text med ett tecken som det inbäddade delmängdstypsnittet aldrig innehöll, eftersom den bytesekvens som skulle välja det tecknet helt enkelt inte finns i typsnittets mappningstabeller. När en PDF-producent bäddar in ett delmängdstypsnitt täcker dess /ToUnicode-CMap och kodningsstrukturer endast de glyfer som det ursprungliga dokumentet faktiskt använde. HPDFEncodeUnicode kan endast vända en mappning som finns: om dokumentet aldrig innehöll bokstaven E i det typsnittet finns det ingen teckenkod för E att vända tillbaka till. Detta är en fysisk egenskap hos filen, inte en begränsning i något specifikt bibliotek — inget verktyg kan frambesvärja en glyfmappning som aldrig bäddades in
HotPDF hanterar misslyckandet konservativt. Om ett enda tecken i ersättningen inte kan omkodas, hoppas hela den förekomsten av sökordet över — inget undantag, ingen partiell skräptext, och förekomsten räknas helt enkelt inte in i ReplaceCount. Den praktiska konsekvensen: kontrollera ReplaceCount mot matchningsantalet från en tidigare sökning, och behandla ett underskott som en signal. I datumexemplet ovan måste siffran 6 förekomma någonstans i dokumentets text i samma typsnitt för att omskrivningen ska lyckas — sannolikt i en faktura, men aldrig garanterat generellt. När tecknen du behöver helt enkelt inte är tillgängliga och målet är att ta bort känslig text snarare än att formulera om den, är verklig innehållsborttagning ändå det bättre verktyget; se redigering och omstrukturering av inlästa PDF-filer i Delphi för den vägen
var
Matches: THPDFTextMatchArray;
Expected, Replaced: Integer;
begin
Pdf.SearchLoadedDocumentText('Acme Corp', True, Matches);
Expected := Length(Matches);
Pdf.ReplaceLoadedDocumentText('Acme Corp', 'Apex Corp', True, Replaced);
if Replaced < Expected then
WriteLn(Format('%d förekomster hoppades över: tecken saknas i typsnittsdelmängden, eller så spänner matchningen över flera operander',
[Expected - Replaced]));
end;
Det andra villkoret för att hoppa över i det meddelandet är den andra dokumenterade gränsen: ett sökord som spänner över flera strängoperander — till exempel Hello uppdelat på [(He)(llo)] TJ-objekt — hittas av sökningen, eftersom sökningen matchar den avkodade glyfsekvensen, men hoppas över av ersättningen, eftersom omskrivning över operandgränser skulle kräva sammanfogning av intilliggande byteintervall. Sök-och-verifiera gör båda gränserna synliga istället för tysta
Vad ändras i filen när du sparar?
En ersatt /Contents-ström sparas okomprimerad. FlateDecode-komprimerade strömmar dekomprimeras för redigering, och när HotPDF skriver de ombyggda byten tar den bort strömmens /Filter-post och uppdaterar /Length istället för att komprimera om. Den resulterande PDF-filen är fullt giltig och renderas normalt i vanliga visare; kompromissen är en större fil för varje redigerad ström. För en batchpipeline som bearbetar tusentals dokument bör du budgetera för den tillväxten eller köra ett separat komprimeringssteg senare. Hur omskrivna objekt interagerar med dokumentets korsreferensstruktur vid sparning är ett eget ämne som behandlas i objektströmmar och inkrementella uppdateringar i HotPDF
Allt annat i filen lämnas ifred. Orörda strömmar behåller sin komprimering, typsnitt och bilder skrivs inte om, och skarvningen på operandnivå innebär att även de redigerade strömmarna endast skiljer sig från originalet där en träff landade. Den konservatismen är avsiktlig: ju mer av ett inläst dokument ett bibliotek skriver om, desto fler möjligheter har det att bryta mot någon egenhet hos producenten som det inte förutsåg
Sök och ersätt av text ansluter sig till extrahering, redigering och sidrendering i HotPDF:s verktygslåda för inlästa dokument, allt drivet av samma tolkare för innehållsströmmar och tillgängligt från Delphi 5 till de nuvarande RAD Studio-utgåvorna utan externa beroenden. Hela API-referensen och testversionen för nedladdning finns på produktsidan för HotPDF Component