diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp index 8fd3ac796..84530352c 100644 --- a/fpdfsdk/fpdf_annot.cpp +++ b/fpdfsdk/fpdf_annot.cpp @@ -3431,4 +3431,767 @@ EPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) { // Build the public handle auto ctx = std::make_unique(dict, IPDFPageFromFPDFPage(page)); return FPDFAnnotationFromCPDFAnnotContext(ctx.release()); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetRotate(FPDF_ANNOTATION annot, float rotation) { + RetainPtr dict = GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!dict) + return false; + + if (rotation == 0.0f) { + // 0 is the default, so remove the key to keep the PDF clean. + dict->RemoveFor("Rotate"); + } else { + dict->SetNewFor("Rotate", rotation); + } + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetRotate(FPDF_ANNOTATION annot, float* rotation) { + if (!rotation) + return false; + + const CPDF_Dictionary* dict = GetAnnotDictFromFPDFAnnotation(annot); + if (!dict) + return false; + + *rotation = dict->GetFloatFor("Rotate"); + return true; +} + +// ============================================================================ +// MEASURE DICTIONARY IMPLEMENTATION +// ============================================================================ + +namespace { + +// Helper: Convert measure array type enum to string key. +const char* MeasureArrayTypeToString(EPDF_MEASURE_ARRAY_TYPE type) { + switch (type) { + case EPDF_MEASURE_X: return "X"; + case EPDF_MEASURE_Y: return "Y"; + case EPDF_MEASURE_D: return "D"; + case EPDF_MEASURE_A: return "A"; + case EPDF_MEASURE_T: return "T"; + case EPDF_MEASURE_S: return "S"; + default: return nullptr; + } +} + +// Helper: Get the Measure dictionary from an annotation (const). +const CPDF_Dictionary* GetMeasureDict(const CPDF_Dictionary* annot_dict) { + if (!annot_dict) + return nullptr; + return annot_dict->GetDictFor("Measure"); +} + +// Helper: Get the Measure dictionary from an annotation (mutable). +RetainPtr GetMutableMeasureDict( + RetainPtr annot_dict) { + if (!annot_dict) + return nullptr; + return annot_dict->GetMutableDictFor("Measure"); +} + +// Helper: Get or create the Measure dictionary. +RetainPtr GetOrCreateMeasureDict( + RetainPtr annot_dict) { + if (!annot_dict) + return nullptr; + RetainPtr measure = annot_dict->GetMutableDictFor("Measure"); + if (!measure) { + measure = annot_dict->SetNewFor("Measure"); + measure->SetNewFor("Type", "Measure"); + measure->SetNewFor("Subtype", "RL"); // Rectilinear + } + return measure; +} + +// Helper: Get the number format array (X, D, or A) from Measure dict (const). +RetainPtr GetMeasureArray(const CPDF_Dictionary* measure, + EPDF_MEASURE_ARRAY_TYPE type) { + const char* key = MeasureArrayTypeToString(type); + if (!measure || !key) + return nullptr; + return measure->GetArrayFor(key); +} + +// Helper: Get the number format array (mutable). +RetainPtr GetMutableMeasureArray( + RetainPtr measure, + EPDF_MEASURE_ARRAY_TYPE type) { + const char* key = MeasureArrayTypeToString(type); + if (!measure || !key) + return nullptr; + return measure->GetMutableArrayFor(key); +} + +// Helper: Get or create the number format array. +RetainPtr GetOrCreateMeasureArray( + RetainPtr measure, + EPDF_MEASURE_ARRAY_TYPE type) { + const char* key = MeasureArrayTypeToString(type); + if (!measure || !key) + return nullptr; + RetainPtr arr = measure->GetMutableArrayFor(key); + if (!arr) + arr = measure->SetNewFor(key); + return arr; +} + +// Helper: Get the number format dictionary at index (const). +const CPDF_Dictionary* GetNumberFormatAt(const CPDF_Array* arr, int index) { + if (!arr || index < 0 || static_cast(index) >= arr->size()) + return nullptr; + return arr->GetDictAt(index); +} + +// Helper: Get the number format dictionary at index (mutable). +RetainPtr GetMutableNumberFormatAt( + RetainPtr arr, + int index) { + if (!arr || index < 0 || static_cast(index) >= arr->size()) + return nullptr; + return arr->GetMutableDictAt(index); +} + +// Helper: Convert string to format style enum. +EPDF_NUMBER_FORMAT_STYLE StringToFormatStyle(const ByteString& name) { + if (name == "F") return EPDF_FORMAT_FRACTIONAL; + if (name == "R") return EPDF_FORMAT_ROUND; + if (name == "T") return EPDF_FORMAT_TRUNCATE; + return EPDF_FORMAT_DECIMAL; // Default: D +} + +// Helper: Convert format style enum to string. +const char* FormatStyleToString(EPDF_NUMBER_FORMAT_STYLE style) { + switch (style) { + case EPDF_FORMAT_FRACTIONAL: return "F"; + case EPDF_FORMAT_ROUND: return "R"; + case EPDF_FORMAT_TRUNCATE: return "T"; + case EPDF_FORMAT_DECIMAL: + default: return "D"; + } +} + +// Helper: Convert string to label position enum. +EPDF_NUMBER_FORMAT_LABEL_POS StringToLabelPosition(const ByteString& name) { + if (name == "P") return EPDF_LABEL_PREFIX; + return EPDF_LABEL_SUFFIX; // Default: S +} + +// Helper: Convert label position enum to string. +const char* LabelPositionToString(EPDF_NUMBER_FORMAT_LABEL_POS pos) { + switch (pos) { + case EPDF_LABEL_PREFIX: return "P"; + case EPDF_LABEL_SUFFIX: + default: return "S"; + } +} + +} // namespace + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_HasMeasure(FPDF_ANNOTATION annot) { + const CPDF_Dictionary* dict = GetAnnotDictFromFPDFAnnotation(annot); + return dict && dict->KeyExist("Measure"); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_CreateMeasure(FPDF_ANNOTATION annot) { + RetainPtr dict = GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!dict) + return false; + GetOrCreateMeasureDict(dict); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_RemoveMeasure(FPDF_ANNOTATION annot) { + RetainPtr dict = GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!dict) + return false; + dict->RemoveFor("Measure"); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureRatio(FPDF_ANNOTATION annot, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + WideString ratio = measure->GetUnicodeTextFor("R"); + return Utf16EncodeMaybeCopyAndReturnLength( + ratio, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureRatio(FPDF_ANNOTATION annot, FPDF_WIDESTRING ratio) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetOrCreateMeasureDict(annot_dict); + if (!measure) + return false; + + WideString ws_ratio = UNSAFE_BUFFERS(WideStringFromFPDFWideString(ratio)); + measure->SetNewFor("R", ws_ratio.AsStringView()); + return true; +} + +FPDF_EXPORT int FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatCount(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + return arr ? fxcrt::CollectionSize(*arr) : 0; +} + +FPDF_EXPORT int FPDF_CALLCONV +EPDFAnnot_AddMeasureFormat(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetOrCreateMeasureDict(annot_dict); + if (!measure) + return -1; + + RetainPtr arr = GetOrCreateMeasureArray(measure, type); + if (!arr) + return -1; + + // Create new number format dictionary with sensible defaults. + // See PDF 32000-1:2008 Table 263 for defaults. + RetainPtr nf = arr->AppendNew(); + nf->SetNewFor("Type", "NumberFormat"); + nf->SetNewFor("C", 1.0f); // Conversion factor + nf->SetNewFor("D", 100); // Denominator (2 decimal places) + nf->SetNewFor("F", "D"); // Format: Decimal + nf->SetNewFor("O", "S"); // Label position: Suffix + nf->SetNewFor("U", ""); // Unit label + nf->SetNewFor("RT", ","); // Thousands separator (default) + nf->SetNewFor("RD", "."); // Decimal separator (default) + nf->SetNewFor("PS", " "); // Prefix spacing (default: space) + nf->SetNewFor("SS", " "); // Suffix spacing (default: space) + + return static_cast(arr->size()) - 1; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_RemoveMeasureFormat(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + if (!arr || index < 0 || static_cast(index) >= arr->size()) + return false; + + arr->RemoveAt(index); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatConversion(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + float* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return false; + + *value = nf->GetFloatFor("C"); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatConversion(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + float value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + nf->SetNewFor("C", value); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + int* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return false; + + *value = nf->GetIntegerFor("D"); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + int value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + nf->SetNewFor("D", value); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatStyle(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_STYLE* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return false; + + *value = StringToFormatStyle(nf->GetNameFor("F")); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatStyle(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_STYLE value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + nf->SetNewFor("F", FormatStyleToString(value)); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatLabelPosition(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_LABEL_POS* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return false; + + *value = StringToLabelPosition(nf->GetNameFor("O")); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatLabelPosition(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_LABEL_POS value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + nf->SetNewFor("O", LabelPositionToString(value)); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatUnit(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return 0; + + WideString unit = nf->GetUnicodeTextFor("U"); + return Utf16EncodeMaybeCopyAndReturnLength( + unit, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatUnit(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING unit) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + WideString ws_unit = UNSAFE_BUFFERS(WideStringFromFPDFWideString(unit)); + nf->SetNewFor("U", ws_unit.AsStringView()); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatPrefix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return 0; + + WideString prefix = nf->GetUnicodeTextFor("PS"); + return Utf16EncodeMaybeCopyAndReturnLength( + prefix, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatPrefix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING prefix) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + WideString ws_prefix = UNSAFE_BUFFERS(WideStringFromFPDFWideString(prefix)); + nf->SetNewFor("PS", ws_prefix.AsStringView()); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatSuffix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return 0; + + WideString suffix = nf->GetUnicodeTextFor("SS"); + return Utf16EncodeMaybeCopyAndReturnLength( + suffix, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatSuffix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING suffix) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + WideString ws_suffix = UNSAFE_BUFFERS(WideStringFromFPDFWideString(suffix)); + nf->SetNewFor("SS", ws_suffix.AsStringView()); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatDecimalSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return 0; + + WideString rd = nf->GetUnicodeTextFor("RD"); + return Utf16EncodeMaybeCopyAndReturnLength( + rd, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatDecimalSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING separator) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + WideString ws_sep = UNSAFE_BUFFERS(WideStringFromFPDFWideString(separator)); + nf->SetNewFor("RD", ws_sep.AsStringView()); + return true; +} + +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatThousandsSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen) { + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return 0; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return 0; + + WideString rt = nf->GetUnicodeTextFor("RT"); + return Utf16EncodeMaybeCopyAndReturnLength( + rt, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatThousandsSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING separator) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + WideString ws_sep = UNSAFE_BUFFERS(WideStringFromFPDFWideString(separator)); + nf->SetNewFor("RT", ws_sep.AsStringView()); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatFixedDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_BOOL* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMeasureArray(measure, type); + const CPDF_Dictionary* nf = GetNumberFormatAt(arr.Get(), index); + if (!nf) + return false; + + *value = nf->GetBooleanFor("FD", false); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatFixedDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_BOOL value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetMutableMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr arr = GetMutableMeasureArray(measure, type); + RetainPtr nf = GetMutableNumberFormatAt(arr, index); + if (!nf) + return false; + + if (value) { + nf->SetNewFor("FD", true); + } else { + nf->RemoveFor("FD"); // false is default, so remove + } + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureOrigin(FPDF_ANNOTATION annot, float* x, float* y) { + if (!x || !y) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr origin = measure->GetArrayFor("O"); + if (!origin || origin->size() < 2) + return false; + + *x = origin->GetFloatAt(0); + *y = origin->GetFloatAt(1); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureOrigin(FPDF_ANNOTATION annot, float x, float y) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetOrCreateMeasureDict(annot_dict); + if (!measure) + return false; + + RetainPtr origin = measure->GetMutableArrayFor("O"); + if (origin) { + origin->Clear(); + } else { + origin = measure->SetNewFor("O"); + } + + origin->AppendNew(x); + origin->AppendNew(y); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureCYX(FPDF_ANNOTATION annot, float* value) { + if (!value) + return false; + + const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); + const CPDF_Dictionary* measure = GetMeasureDict(annot_dict); + if (!measure) + return false; + + if (!measure->KeyExist("CYX")) + return false; + + *value = measure->GetFloatFor("CYX"); + return true; +} + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureCYX(FPDF_ANNOTATION annot, float value) { + RetainPtr annot_dict = + GetMutableAnnotDictFromFPDFAnnotation(annot); + RetainPtr measure = GetOrCreateMeasureDict(annot_dict); + if (!measure) + return false; + + measure->SetNewFor("CYX", value); + return true; } \ No newline at end of file diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h index 8c9b1de05..61b24d308 100644 --- a/public/fpdf_annot.h +++ b/public/fpdf_annot.h @@ -217,6 +217,31 @@ typedef enum EPDF_STAMP_FIT { EPDF_STAMP_FIT_STRETCH = 2 // ignore aspect, fill box } EPDF_STAMP_FIT; +// Measure dictionary array types (X, Y, D, A, T, S) +typedef enum EPDF_MEASURE_ARRAY_TYPE { + EPDF_MEASURE_X = 0, // X-axis: points to display units conversion + EPDF_MEASURE_Y = 1, // Y-axis: when different units/scale from X + EPDF_MEASURE_D = 2, // Distance display format + EPDF_MEASURE_A = 3, // Area display format + EPDF_MEASURE_T = 4, // Angle (theta) display format + EPDF_MEASURE_S = 5 // Slope display format +} EPDF_MEASURE_ARRAY_TYPE; + +// Number format style (F key in number format dict) +typedef enum EPDF_NUMBER_FORMAT_STYLE { + EPDF_FORMAT_DECIMAL = 0, // D - decimal + EPDF_FORMAT_FRACTIONAL = 1, // F - fractional (e.g., 1/4) + EPDF_FORMAT_ROUND = 2, // R - round to integer + EPDF_FORMAT_TRUNCATE = 3 // T - truncate to integer +} EPDF_NUMBER_FORMAT_STYLE; + +// Number format label position (O key in number format dict) +// Specifies where the unit label appears relative to the value +typedef enum EPDF_NUMBER_FORMAT_LABEL_POS { + EPDF_LABEL_SUFFIX = 0, // S - label after value (default), e.g., "5 in" + EPDF_LABEL_PREFIX = 1 // P - label before value, e.g., "$5" +} EPDF_NUMBER_FORMAT_LABEL_POS; + // Experimental API. // Check if an annotation subtype is currently supported for creation. // Currently supported subtypes: @@ -1593,6 +1618,485 @@ EPDFAnnot_UpdateAppearanceToRect(FPDF_ANNOTATION annot, EPDF_STAMP_FIT fit); FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV EPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype); +// Experimental EmbedPDF Extension API. +// Set the rotation of an annotation in degrees. +// +// annot - handle to an annotation. +// rotation - the rotation in degrees (any value, e.g. 0, 45.5, 90, 180, etc.). +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetRotate(FPDF_ANNOTATION annot, float rotation); + +// Experimental EmbedPDF Extension API. +// Get the rotation of an annotation in degrees. +// +// annot - handle to an annotation. +// rotation - receives the rotation value in degrees. +// +// Returns true on success, false if annot is invalid or rotation is NULL. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetRotate(FPDF_ANNOTATION annot, float* rotation); + +// ============================================================================ +// MEASURE DICTIONARY API +// Used for measurement annotations (distance, area, etc.) +// ============================================================================ + +// Experimental EmbedPDF Extension API. +// Check if the annotation has a Measure dictionary. +// +// annot - handle to an annotation. +// +// Returns true if the annotation has a Measure dictionary. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_HasMeasure(FPDF_ANNOTATION annot); + +// Experimental EmbedPDF Extension API. +// Create a Measure dictionary on the annotation if it doesn't exist. +// +// annot - handle to an annotation. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_CreateMeasure(FPDF_ANNOTATION annot); + +// Experimental EmbedPDF Extension API. +// Remove the Measure dictionary from the annotation. +// +// annot - handle to an annotation. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_RemoveMeasure(FPDF_ANNOTATION annot); + +// Experimental EmbedPDF Extension API. +// Get the ratio string (R key) from the Measure dictionary. +// Example: "1 in = 1 in" +// +// annot - handle to an annotation. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed (including null terminator). +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureRatio(FPDF_ANNOTATION annot, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the ratio string (R key) in the Measure dictionary. +// +// annot - handle to an annotation. +// ratio - the ratio string (UTF-16LE), e.g., L"1 in = 1 in". +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureRatio(FPDF_ANNOTATION annot, FPDF_WIDESTRING ratio); + +// Experimental EmbedPDF Extension API. +// Get the count of number format entries in an array (X, D, or A). +// +// annot - handle to an annotation. +// type - which array: EPDF_MEASURE_X, EPDF_MEASURE_D, or EPDF_MEASURE_A. +// +// Returns the count, or 0 if not found. +FPDF_EXPORT int FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatCount(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type); + +// Experimental EmbedPDF Extension API. +// Add a new number format entry to an array (X, D, or A). +// Creates the array if it doesn't exist. +// The new entry is initialized with sensible defaults: +// C=1.0, D=100, F=Decimal, O=Simple, RD="." +// +// annot - handle to an annotation. +// type - which array: EPDF_MEASURE_X, EPDF_MEASURE_D, or EPDF_MEASURE_A. +// +// Returns the index of the new entry, or -1 on failure. +FPDF_EXPORT int FPDF_CALLCONV +EPDFAnnot_AddMeasureFormat(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type); + +// Experimental EmbedPDF Extension API. +// Remove a number format entry from an array at the specified index. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry to remove. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_RemoveMeasureFormat(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index); + +// Experimental EmbedPDF Extension API. +// Get the conversion factor (C key) from a number format entry. +// This is the factor to convert from PDF points to the display unit. +// Example: 0.0138889 converts points to inches (1/72). +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - receives the conversion factor. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatConversion(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + float* value); + +// Experimental EmbedPDF Extension API. +// Set the conversion factor (C key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - the conversion factor. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatConversion(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + float value); + +// Experimental EmbedPDF Extension API. +// Get the denominator/precision (D key) from a number format entry. +// Controls decimal precision (e.g., 100 = two decimal places). +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - receives the denominator. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + int* value); + +// Experimental EmbedPDF Extension API. +// Set the denominator/precision (D key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - the denominator. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + int value); + +// Experimental EmbedPDF Extension API. +// Get the format style (F key) from a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - receives the format style. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatStyle(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_STYLE* value); + +// Experimental EmbedPDF Extension API. +// Set the format style (F key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - the format style. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatStyle(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_STYLE value); + +// Experimental EmbedPDF Extension API. +// Get the label position (O key) from a number format entry. +// Specifies whether the unit label appears before or after the value. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - receives the label position. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatLabelPosition(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_LABEL_POS* value); + +// Experimental EmbedPDF Extension API. +// Set the label position (O key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - the label position. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatLabelPosition(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + EPDF_NUMBER_FORMAT_LABEL_POS value); + +// Experimental EmbedPDF Extension API. +// Get the unit label (U key) from a number format entry. +// Example: "in", "cm", "ft", "m" +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed. +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatUnit(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the unit label (U key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// unit - the unit string (UTF-16LE). +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatUnit(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING unit); + +// Experimental EmbedPDF Extension API. +// Get the prefix string (PS key) from a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed. +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatPrefix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the prefix string (PS key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// prefix - the prefix string (UTF-16LE). +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatPrefix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING prefix); + +// Experimental EmbedPDF Extension API. +// Get the suffix string (SS key) from a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed. +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatSuffix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the suffix string (SS key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// suffix - the suffix string (UTF-16LE). +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatSuffix(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING suffix); + +// Experimental EmbedPDF Extension API. +// Get the decimal separator (RD key) from a number format entry. +// Usually "." but can be "," for European locales. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed. +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatDecimalSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the decimal separator (RD key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// separator - the decimal separator (UTF-16LE), e.g., L"." or L",". +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatDecimalSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING separator); + +// Experimental EmbedPDF Extension API. +// Get the thousands separator (RT key) from a number format entry. +// Default is "," (comma). +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// buffer - buffer to receive the UTF-16LE encoded string. +// buflen - length of buffer in bytes. +// +// Returns the number of bytes needed. +FPDF_EXPORT unsigned long FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatThousandsSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WCHAR* buffer, + unsigned long buflen); + +// Experimental EmbedPDF Extension API. +// Set the thousands separator (RT key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// separator - the thousands separator (UTF-16LE), e.g., L"," or L".". +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatThousandsSeparator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_WIDESTRING separator); + +// Experimental EmbedPDF Extension API. +// Get the fixed denominator flag (FD key) from a number format entry. +// When true, fractional values may not have denominator reduced or +// trailing zeros truncated. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - receives the fixed denominator flag. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureFormatFixedDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_BOOL* value); + +// Experimental EmbedPDF Extension API. +// Set the fixed denominator flag (FD key) in a number format entry. +// +// annot - handle to an annotation. +// type - which array. +// index - index of the entry. +// value - the fixed denominator flag. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureFormatFixedDenominator(FPDF_ANNOTATION annot, + EPDF_MEASURE_ARRAY_TYPE type, + int index, + FPDF_BOOL value); + +// Experimental EmbedPDF Extension API. +// Get the origin (O key) from the Measure dictionary. +// The origin specifies the starting point of the measurement coordinate +// system in default user space coordinates. +// +// annot - handle to an annotation. +// x - receives the x coordinate of the origin. +// y - receives the y coordinate of the origin. +// +// Returns true on success, false if origin is not set. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureOrigin(FPDF_ANNOTATION annot, float* x, float* y); + +// Experimental EmbedPDF Extension API. +// Set the origin (O key) in the Measure dictionary. +// +// annot - handle to an annotation. +// x - the x coordinate of the origin. +// y - the y coordinate of the origin. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureOrigin(FPDF_ANNOTATION annot, float x, float y); + +// Experimental EmbedPDF Extension API. +// Get the Y-to-X conversion factor (CYX key) from the Measure dictionary. +// Used to convert largest Y units to largest X units for calculations +// when X and Y have different scales. +// +// annot - handle to an annotation. +// value - receives the CYX value. +// +// Returns true on success, false if CYX is not set. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_GetMeasureCYX(FPDF_ANNOTATION annot, float* value); + +// Experimental EmbedPDF Extension API. +// Set the Y-to-X conversion factor (CYX key) in the Measure dictionary. +// +// annot - handle to an annotation. +// value - the CYX value. +// +// Returns true on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +EPDFAnnot_SetMeasureCYX(FPDF_ANNOTATION annot, float value); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus