HotPDF는 Delphi 및 C++Builder 애플리케이션을 위한 네이티브 VCL PDF 라이브러리입니다. 외부 PDF 런타임 배포 없이 PDF 생성, 편집, 양식, 주석, 암호화, 디지털 서명, Unicode 글꼴 처리, 표준 지향 출력, 프리플라이트 보고를 지원합니다
이 글은 developers replacing hand-edited PDF forms with deterministic Delphi form generation을 위한 글입니다. AcroForm fields and action logic을 단순한 컴포넌트 호출이 아니라 운영 환경의 문서 엔지니어링으로 다룹니다
실제 위험은 field widgets can look correct while shared names, export values, JavaScript actions, tab order, or flattening rules break the receiving workflow입니다. 따라서 명확한 계약, 관찰 가능한 진단, 실제 고객 파일을 반영한 회귀 샘플이 필요합니다
아키텍처 결정
Treat the field map as an application contract. field naming rules for repeated widgets and grouped business values / allowed trigger actions, submit targets, and viewer-side script policy
- field naming rules for repeated widgets and grouped business values
- allowed trigger actions, submit targets, and viewer-side script policy
- required flags, default values, calculation order, and validation messages
- whether the output remains interactive or is flattened for archive delivery
구현 흐름
Build the form layer before assigning values. The order below keeps the workflow reviewable for Delphi and C++Builder teams
- inventory the template fields and normalize names before binding application data
- apply values only after the allowed action profile has been selected
- refresh widget appearances with the same font and color policy used at design time
- validate exported values and tab order in the same viewer family used by customers
- flatten only after all required-field and calculation checks have passed
검증 증거
Evidence that proves the form is usable. Keep these fields with the output or support record
- field name, widget bounds, page number, required state, and exported value
- action type, trigger event, destination, and whether the profile allowed it
- appearance stream status, font fallback decision, and calculated-field result
- remaining interactive fields after flattening and warnings for unsupported actions
Appearance streams, actions, and flattening order
A production form workflow separates field creation, value assignment, appearance refresh, action binding, and final flattening. Keeping those phases visible makes it possible to explain why a required field failed or why a submit action was suppressed
Decision table for AcroForm fields and action logic
A decision table keeps product ownership visible when the same workflow is reused by a desktop tool, service job, and support utility
| Decision | Engineering reason | Evidence |
|---|---|---|
| field naming rules for repeated widgets and grouped business values | inventory the template fields and normalize names before binding application data | field name, widget bounds, page number, required state, and exported value |
| allowed trigger actions, submit targets, and viewer-side script policy | apply values only after the allowed action profile has been selected | action type, trigger event, destination, and whether the profile allowed it |
| required flags, default values, calculation order, and validation messages | refresh widget appearances with the same font and color policy used at design time | appearance stream status, font fallback decision, and calculated-field result |
AcroForm fields and action logic에 대한 엔지니어링 검토 노트
이 검토 노트를 사용해 기능이 데모 단계를 넘어섰고 출시, 지원, 고객 에스컬레이션 상황에서 설명할 수 있는지 확인합니다
- 결정: field naming rules for repeated widgets and grouped business values. 구현상 핵심 지점: apply values only after the allowed action profile has been selected. 승인 증거: appearance stream status, font fallback decision, and calculated-field result. 회귀 트리거: flattening before validation can permanently hide incomplete or inconsistent data
- 결정: allowed trigger actions, submit targets, and viewer-side script policy. 구현상 핵심 지점: refresh widget appearances with the same font and color policy used at design time. 승인 증거: remaining interactive fields after flattening and warnings for unsupported actions. 회귀 트리거: checkbox captions do not always match the export values consumed by external systems
경계 사례
- checkbox captions do not always match the export values consumed by external systems
- identically named fields may intentionally share one value across several pages
- viewer security settings can block actions that worked during development
- flattening before validation can permanently hide incomplete or inconsistent data
Delphi / C++Builder 참고 사항
HotPDF Component should sit behind a small service boundary that receives files, streams, profiles, and credentials, then returns output paths, warnings, metrics, and validation status. 중요한 용어는 AcroForm, widget, field action, appearance stream, submit action, flattening
Delphi 코드 예제
다음 Delphi 스케치는 이 주제에 맞는 실무형 서비스 경계를 보여 줍니다. 정책 검사, 로깅, 검증을 좁은 제품 호출 구간 밖에 두면 워크플로를 테스트하기 쉽습니다
procedure BuildAcroFormPackage(const OutputFile: string; const Profile: TFormProfile);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := OutputFile;
Pdf.BeginDoc;
AddCustomerFields(Pdf, Profile);
WireSubmitActions(Pdf, Profile.ActionMap);
ValidateRequiredFields(Profile.RequiredFields);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
운영 체크리스트
- 워크플로는 빈 파일, 일반 고객 파일, 최악의 파일에서 실행합니다
- 생성된 PDF는 대상 뷰어, 검증기, 프린터 또는 downstream 애플리케이션에서 엽니다
- 제품 버전, 프로필 버전, 입력 해시, 출력 경로, 경과 시간, 경고 수를 기록합니다
- 암호, 인증서, 임시 파일, 고객 데이터는 명확한 보존 규칙에 따라 관리합니다
- 고객 파일이 새로운 경계 사례를 드러내면 회귀 문서를 추가합니다
제품 문서
추가 코드 예제
// Open a help page in the system browser
Pdf.CurrentPage.AddPushButtonWithAction('btnHelp', 'Help',
'https://www.example.com/claims-help', Rect(320, 700, 420, 730), baURI);
// Run viewer-side JavaScript
Pdf.CurrentPage.AddPushButtonWithAction('btnRecalc', 'Recalculate',
'app.alert("Totals updated.");', Rect(320, 660, 420, 690), baJavaScript);
// Submit as XFDF and keep empty fields in the payload
Pdf.CurrentPage.AddPushButtonWithSubmitAction('btnSubmit', 'Submit claim',
'https://api.example.com/claims', Rect(320, 620, 420, 650),
[sffXFDF, sffIncludeNoValueFields]);// Reject committed values that are not plausible email addresses
Pdf.AttachFieldKeyStrokeAction('applicant.email',
'if (event.willCommit && !/^[\w.-]+@[\w.-]+\.\w+$/.test(event.value)) event.rc = false;');
// Display US phone numbers as (NNN) NNN-NNNN
Pdf.AttachFieldFormatAction('applicant.phone',
'event.value = event.value.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");');
// Refuse applicants under 18 at commit time
Pdf.AttachFieldValidateAction('applicant.age',
'if (parseInt(event.value) < 18) event.rc = false;');