| 1 | unit BGRAUnits;
|
|---|
| 2 |
|
|---|
| 3 | {$mode objfpc}{$H+}
|
|---|
| 4 |
|
|---|
| 5 | interface
|
|---|
| 6 |
|
|---|
| 7 | uses
|
|---|
| 8 | Classes, SysUtils, BGRABitmapTypes;
|
|---|
| 9 |
|
|---|
| 10 | type
|
|---|
| 11 | TCSSUnit = (cuCustom, cuPixel,
|
|---|
| 12 | cuCentimeter, cuMillimeter,
|
|---|
| 13 | cuInch, cuPica, cuPoint,
|
|---|
| 14 | cuFontEmHeight, cuFontXHeight, cuPercent);
|
|---|
| 15 | TFloatWithCSSUnit = record
|
|---|
| 16 | value: single;
|
|---|
| 17 | CSSUnit: TCSSUnit;
|
|---|
| 18 | end;
|
|---|
| 19 |
|
|---|
| 20 | function FloatWithCSSUnit(AValue: single; AUnit: TCSSUnit): TFloatWithCSSUnit;
|
|---|
| 21 |
|
|---|
| 22 | const
|
|---|
| 23 | CSSUnitShortName: array[TCSSUnit] of string =
|
|---|
| 24 | ('','px',
|
|---|
| 25 | 'cm','mm',
|
|---|
| 26 | 'in','pc','pt',
|
|---|
| 27 | 'em','ex','%');
|
|---|
| 28 |
|
|---|
| 29 | type
|
|---|
| 30 | { TCSSUnitConverter }
|
|---|
| 31 |
|
|---|
| 32 | TCSSUnitConverter = class
|
|---|
| 33 | protected
|
|---|
| 34 | function GetDefaultUnitHeight: TFloatWithCSSUnit; virtual;
|
|---|
| 35 | function GetDefaultUnitWidth: TFloatWithCSSUnit; virtual;
|
|---|
| 36 | function GetDpiScaleTransform: string;
|
|---|
| 37 | function GetDpiX: single; virtual;
|
|---|
| 38 | function GetDpiY: single; virtual;
|
|---|
| 39 | function GetDPIScaled: boolean; virtual;
|
|---|
| 40 | function GetDpiScaleX: single; virtual;
|
|---|
| 41 | function GetDpiScaleY: single; virtual;
|
|---|
| 42 | function GetFontEmHeight: TFloatWithCSSUnit; virtual;
|
|---|
| 43 | function GetFontXHeight: TFloatWithCSSUnit; virtual;
|
|---|
| 44 | property FontEmHeight: TFloatWithCSSUnit read GetFontEmHeight;
|
|---|
| 45 | property FontXHeight: TFloatWithCSSUnit read GetFontXHeight;
|
|---|
| 46 | property DefaultUnitWidth: TFloatWithCSSUnit read GetDefaultUnitWidth;
|
|---|
| 47 | property DefaultUnitHeight: TFloatWithCSSUnit read GetDefaultUnitHeight;
|
|---|
| 48 | public
|
|---|
| 49 | function Convert(xy: single; sourceUnit, destUnit: TCSSUnit; dpi: single; containerSize: single = 0): single;
|
|---|
| 50 | function ConvertWidth(x: single; sourceUnit, destUnit: TCSSUnit; containerWidth: single = 0): single; overload;
|
|---|
| 51 | function ConvertHeight(y: single; sourceUnit, destUnit: TCSSUnit; containerHeight: single = 0): single; overload;
|
|---|
| 52 | function ConvertWidth(AValue: TFloatWithCSSUnit; destUnit: TCSSUnit; containerWidth: single = 0): TFloatWithCSSUnit; overload;
|
|---|
| 53 | function ConvertHeight(AValue: TFloatWithCSSUnit; destUnit: TCSSUnit; containerHeight: single = 0): TFloatWithCSSUnit; overload;
|
|---|
| 54 | function ConvertCoord(pt: TPointF; sourceUnit, destUnit: TCSSUnit; containerWidth: single = 0; containerHeight: single = 0): TPointF; virtual;
|
|---|
| 55 | class function parseValue(AValue: string; ADefault: TFloatWithCSSUnit): TFloatWithCSSUnit; overload;
|
|---|
| 56 | class function parseValue(AValue: string; ADefault: single): single; overload;
|
|---|
| 57 | class function formatValue(AValue: TFloatWithCSSUnit; APrecision: integer = 7): string; overload;
|
|---|
| 58 | class function formatValue(AValue: single; APrecision: integer = 7): string; overload;
|
|---|
| 59 | property DpiX: single read GetDpiX;
|
|---|
| 60 | property DpiY: single read GetDpiY;
|
|---|
| 61 | property DpiScaled: boolean read GetDPIScaled;
|
|---|
| 62 | property DpiScaleX: single read GetDpiScaleX;
|
|---|
| 63 | property DpiScaleY: single read GetDpiScaleY;
|
|---|
| 64 | property DpiScaleTransform: string read GetDpiScaleTransform;
|
|---|
| 65 | end;
|
|---|
| 66 |
|
|---|
| 67 | implementation
|
|---|
| 68 |
|
|---|
| 69 | var
|
|---|
| 70 | formats: TFormatSettings;
|
|---|
| 71 |
|
|---|
| 72 | const InchFactor: array[TCSSUnit] of integer =
|
|---|
| 73 | (9600, 9600,
|
|---|
| 74 | 254, 2540,
|
|---|
| 75 | 100, 600, 7200,
|
|---|
| 76 | 0, 0, 0);
|
|---|
| 77 |
|
|---|
| 78 | function FloatWithCSSUnit(AValue: single; AUnit: TCSSUnit): TFloatWithCSSUnit;
|
|---|
| 79 | begin
|
|---|
| 80 | result.value:= AValue;
|
|---|
| 81 | result.CSSUnit:= AUnit;
|
|---|
| 82 | end;
|
|---|
| 83 |
|
|---|
| 84 | { TCSSUnitConverter }
|
|---|
| 85 |
|
|---|
| 86 | function TCSSUnitConverter.GetDpiScaleX: single;
|
|---|
| 87 | begin
|
|---|
| 88 | result := 1;
|
|---|
| 89 | end;
|
|---|
| 90 |
|
|---|
| 91 | function TCSSUnitConverter.GetDpiScaleY: single;
|
|---|
| 92 | begin
|
|---|
| 93 | result := 1;
|
|---|
| 94 | end;
|
|---|
| 95 |
|
|---|
| 96 | function TCSSUnitConverter.GetFontEmHeight: TFloatWithCSSUnit;
|
|---|
| 97 | begin
|
|---|
| 98 | result := FloatWithCSSUnit(0,cuCustom);
|
|---|
| 99 | end;
|
|---|
| 100 |
|
|---|
| 101 | function TCSSUnitConverter.GetFontXHeight: TFloatWithCSSUnit;
|
|---|
| 102 | begin
|
|---|
| 103 | result := FloatWithCSSUnit(0,cuCustom);
|
|---|
| 104 | end;
|
|---|
| 105 |
|
|---|
| 106 | function TCSSUnitConverter.GetDPIScaled: boolean;
|
|---|
| 107 | begin
|
|---|
| 108 | result := false;
|
|---|
| 109 | end;
|
|---|
| 110 |
|
|---|
| 111 | function TCSSUnitConverter.GetDpiScaleTransform: string;
|
|---|
| 112 | begin
|
|---|
| 113 | result := 'scale('+formatValue(DpiScaleX)+','+
|
|---|
| 114 | formatValue(DpiScaleY)+')';
|
|---|
| 115 | end;
|
|---|
| 116 |
|
|---|
| 117 | function TCSSUnitConverter.GetDefaultUnitHeight: TFloatWithCSSUnit;
|
|---|
| 118 | begin
|
|---|
| 119 | result := FloatWithCSSUnit(1,cuPixel);
|
|---|
| 120 | end;
|
|---|
| 121 |
|
|---|
| 122 | function TCSSUnitConverter.GetDefaultUnitWidth: TFloatWithCSSUnit;
|
|---|
| 123 | begin
|
|---|
| 124 | result := FloatWithCSSUnit(1,cuPixel);
|
|---|
| 125 | end;
|
|---|
| 126 |
|
|---|
| 127 | function TCSSUnitConverter.GetDpiX: single;
|
|---|
| 128 | begin
|
|---|
| 129 | result := 96;
|
|---|
| 130 | end;
|
|---|
| 131 |
|
|---|
| 132 | function TCSSUnitConverter.GetDpiY: single;
|
|---|
| 133 | begin
|
|---|
| 134 | result := 96;
|
|---|
| 135 | end;
|
|---|
| 136 |
|
|---|
| 137 | function TCSSUnitConverter.Convert(xy: single; sourceUnit, destUnit: TCSSUnit;
|
|---|
| 138 | dpi: single; containerSize: single): single;
|
|---|
| 139 | var sourceFactor, destFactor: integer;
|
|---|
| 140 | begin
|
|---|
| 141 | //fallback values for cuCustom as pixels
|
|---|
| 142 | if sourceUnit = cuCustom then sourceUnit := cuPixel;
|
|---|
| 143 | if destUnit = cuCustom then destUnit := cuPixel;
|
|---|
| 144 | if (sourceUnit = destUnit) then
|
|---|
| 145 | result := xy
|
|---|
| 146 | else
|
|---|
| 147 | if sourceUnit = cuPercent then
|
|---|
| 148 | begin
|
|---|
| 149 | result := xy/100*containerSize;
|
|---|
| 150 | end else
|
|---|
| 151 | if sourceUnit = cuFontEmHeight then
|
|---|
| 152 | begin
|
|---|
| 153 | with FontEmHeight do result := Convert(xy*value,CSSUnit, destUnit, dpi);
|
|---|
| 154 | end else
|
|---|
| 155 | if sourceUnit = cuFontXHeight then
|
|---|
| 156 | begin
|
|---|
| 157 | with FontXHeight do result := Convert(xy*value,CSSUnit, destUnit, dpi);
|
|---|
| 158 | end else
|
|---|
| 159 | if destUnit = cuFontEmHeight then
|
|---|
| 160 | begin
|
|---|
| 161 | with FontEmHeight do
|
|---|
| 162 | if value = 0 then result := 0
|
|---|
| 163 | else result := Convert(xy/value,sourceUnit, CSSUnit, dpi);
|
|---|
| 164 | end else
|
|---|
| 165 | if destUnit = cuFontEmHeight then
|
|---|
| 166 | begin
|
|---|
| 167 | with FontXHeight do
|
|---|
| 168 | if value = 0 then result := 0
|
|---|
| 169 | else result := Convert(xy/value,sourceUnit, CSSUnit, dpi);
|
|---|
| 170 | end else
|
|---|
| 171 | if sourceUnit = cuPixel then
|
|---|
| 172 | begin
|
|---|
| 173 | if dpi = 0 then result := 0
|
|---|
| 174 | else result := xy*(InchFactor[sourceUnit]/(dpi*100));
|
|---|
| 175 | end else
|
|---|
| 176 | if destUnit = cuPixel then
|
|---|
| 177 | begin
|
|---|
| 178 | if dpi = 0 then result := 0
|
|---|
| 179 | else result := xy*((dpi*100)/InchFactor[sourceUnit]);
|
|---|
| 180 | end else
|
|---|
| 181 | begin
|
|---|
| 182 | sourceFactor := InchFactor[sourceUnit];
|
|---|
| 183 | destFactor := InchFactor[destUnit];
|
|---|
| 184 | if (sourceFactor = 0) or (destFactor = 0) then
|
|---|
| 185 | result := 0
|
|---|
| 186 | else
|
|---|
| 187 | result := xy*(destFactor/sourceFactor);
|
|---|
| 188 | end;
|
|---|
| 189 | end;
|
|---|
| 190 |
|
|---|
| 191 | function TCSSUnitConverter.ConvertWidth(x: single; sourceUnit,
|
|---|
| 192 | destUnit: TCSSUnit; containerWidth: single): single;
|
|---|
| 193 | begin
|
|---|
| 194 | if sourceUnit = destUnit then
|
|---|
| 195 | result := x
|
|---|
| 196 | else if sourceUnit = cuCustom then
|
|---|
| 197 | with DefaultUnitWidth do
|
|---|
| 198 | begin
|
|---|
| 199 | result := x*ConvertWidth(value,CSSUnit, destUnit, containerWidth)
|
|---|
| 200 | end
|
|---|
| 201 | else if destUnit = cuCustom then
|
|---|
| 202 | with ConvertWidth(DefaultUnitWidth,sourceUnit) do
|
|---|
| 203 | begin
|
|---|
| 204 | if value = 0 then
|
|---|
| 205 | result := 0
|
|---|
| 206 | else
|
|---|
| 207 | result := x/value;
|
|---|
| 208 | end else
|
|---|
| 209 | result := Convert(x, sourceUnit, destUnit, DpiX, containerWidth);
|
|---|
| 210 | end;
|
|---|
| 211 |
|
|---|
| 212 | function TCSSUnitConverter.ConvertHeight(y: single; sourceUnit,
|
|---|
| 213 | destUnit: TCSSUnit; containerHeight: single): single;
|
|---|
| 214 | begin
|
|---|
| 215 | if sourceUnit = cuCustom then
|
|---|
| 216 | with DefaultUnitHeight do
|
|---|
| 217 | begin
|
|---|
| 218 | result := y*ConvertHeight(value,CSSUnit, destUnit, containerHeight)
|
|---|
| 219 | end
|
|---|
| 220 | else if destUnit = cuCustom then
|
|---|
| 221 | with ConvertHeight(DefaultUnitHeight,sourceUnit) do
|
|---|
| 222 | begin
|
|---|
| 223 | if value = 0 then
|
|---|
| 224 | result := 0
|
|---|
| 225 | else
|
|---|
| 226 | result := y/value;
|
|---|
| 227 | end else
|
|---|
| 228 | result := Convert(y, sourceUnit, destUnit, DpiY, containerHeight);
|
|---|
| 229 | end;
|
|---|
| 230 |
|
|---|
| 231 | function TCSSUnitConverter.ConvertWidth(AValue: TFloatWithCSSUnit;
|
|---|
| 232 | destUnit: TCSSUnit; containerWidth: single): TFloatWithCSSUnit;
|
|---|
| 233 | begin
|
|---|
| 234 | result.CSSUnit := destUnit;
|
|---|
| 235 | result.value:= ConvertWidth(AValue.value,AValue.CSSUnit,destUnit,containerWidth);
|
|---|
| 236 | end;
|
|---|
| 237 |
|
|---|
| 238 | function TCSSUnitConverter.ConvertHeight(AValue: TFloatWithCSSUnit;
|
|---|
| 239 | destUnit: TCSSUnit; containerHeight: single): TFloatWithCSSUnit;
|
|---|
| 240 | begin
|
|---|
| 241 | result.CSSUnit := destUnit;
|
|---|
| 242 | result.value:= ConvertHeight(AValue.value,AValue.CSSUnit,destUnit,containerHeight);
|
|---|
| 243 | end;
|
|---|
| 244 |
|
|---|
| 245 | function TCSSUnitConverter.ConvertCoord(pt: TPointF; sourceUnit,
|
|---|
| 246 | destUnit: TCSSUnit; containerWidth: single; containerHeight: single): TPointF;
|
|---|
| 247 | begin
|
|---|
| 248 | result.x := ConvertWidth(pt.x, sourceUnit, destUnit, containerWidth);
|
|---|
| 249 | result.y := ConvertHeight(pt.y, sourceUnit, destUnit, containerHeight);
|
|---|
| 250 | end;
|
|---|
| 251 |
|
|---|
| 252 | class function TCSSUnitConverter.parseValue(AValue: string;
|
|---|
| 253 | ADefault: TFloatWithCSSUnit): TFloatWithCSSUnit;
|
|---|
| 254 | var cssUnit: TCSSUnit;
|
|---|
| 255 | errPos: integer;
|
|---|
| 256 | begin
|
|---|
| 257 | AValue := trim(AValue);
|
|---|
| 258 | result.CSSUnit:= cuCustom;
|
|---|
| 259 | for cssUnit := succ(cuCustom) to high(cssUnit) do
|
|---|
| 260 | if (length(AValue)>=length(CSSUnitShortName[cssUnit])) and
|
|---|
| 261 | (CompareText(copy(AValue,length(AValue)-length(CSSUnitShortName[cssUnit])+1,length(CSSUnitShortName[cssUnit])),
|
|---|
| 262 | CSSUnitShortName[cssUnit])=0) then
|
|---|
| 263 | begin
|
|---|
| 264 | AValue := copy(AValue,1,length(AValue)-length(CSSUnitShortName[cssUnit]));
|
|---|
| 265 | result.CSSUnit := cssUnit;
|
|---|
| 266 | break;
|
|---|
| 267 | end;
|
|---|
| 268 | val(AValue,result.value,errPos);
|
|---|
| 269 | if errPos <> 0 then
|
|---|
| 270 | result := ADefault;
|
|---|
| 271 | end;
|
|---|
| 272 |
|
|---|
| 273 | class function TCSSUnitConverter.parseValue(AValue: string; ADefault: single): single;
|
|---|
| 274 | var
|
|---|
| 275 | errPos: integer;
|
|---|
| 276 | begin
|
|---|
| 277 | AValue := trim(AValue);
|
|---|
| 278 | val(AValue,result,errPos);
|
|---|
| 279 | if errPos <> 0 then
|
|---|
| 280 | result := ADefault;
|
|---|
| 281 | end;
|
|---|
| 282 |
|
|---|
| 283 | class function TCSSUnitConverter.formatValue(AValue: TFloatWithCSSUnit; APrecision: integer = 7): string;
|
|---|
| 284 | begin
|
|---|
| 285 | result := FloatToStrF(AValue.value,ffGeneral,APrecision,0,formats)+CSSUnitShortName[AValue.CSSUnit];
|
|---|
| 286 | end;
|
|---|
| 287 |
|
|---|
| 288 | class function TCSSUnitConverter.formatValue(AValue: single; APrecision: integer
|
|---|
| 289 | ): string;
|
|---|
| 290 | begin
|
|---|
| 291 | result := FloatToStrF(AValue,ffGeneral,APrecision,0,formats);
|
|---|
| 292 | end;
|
|---|
| 293 |
|
|---|
| 294 | initialization
|
|---|
| 295 |
|
|---|
| 296 | formats := DefaultFormatSettings;
|
|---|
| 297 | formats.DecimalSeparator := '.';
|
|---|
| 298 |
|
|---|
| 299 | end.
|
|---|
| 300 |
|
|---|