From 05dd81a53901331f90845916776389aaa9287ad4 Mon Sep 17 00:00:00 2001 From: Bob Singor Date: Fri, 2 Jan 2026 15:11:07 +0200 Subject: [PATCH] Add annotation rotation and measure dictionary APIs Introduces experimental APIs for setting and getting annotation rotation, and for creating, modifying, and querying Measure dictionaries on annotations. This includes support for number format arrays, conversion factors, unit labels, decimal/thousands separators, label positions, and origin/cyx values, enabling advanced measurement annotation features. --- fpdfsdk/fpdf_annot.cpp | 763 +++++++++++++++++++++++++++++++++++++++++ public/fpdf_annot.h | 504 +++++++++++++++++++++++++++ 2 files changed, 1267 insertions(+) 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