1/* * 2 * 3 * (c) 2010-2024 Torstein Honsi 4 * 5 * License: www.highcharts.com/license 6 * 7 * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! 8 * 9 * */ 10'use strict'; 11import A from '../../Core/Animation/AnimationUtilities.js'; 12const { animObject } = A; 13import Color from '../../Core/Color/Color.js'; 14const { parse: color } = Color; 15import ColumnSeriesDefaults from './ColumnSeriesDefaults.js'; 16import H from '../../Core/Globals.js'; 17const { noop } = H; 18import Series from '../../Core/Series/Series.js'; 19import SeriesRegistry from '../../Core/Series/SeriesRegistry.js'; 20import U from '../../Core/Utilities.js'; 21const { clamp, crisp, defined, extend, fireEvent, isArray, isNumber, merge, pick, objectEach } = U; 22/* * 23 * 24 * Class 25 * 26 * */ 27/** 28 * The column series type. 29 * 30 * @private 31 * @class 32 * @name Highcharts.seriesTypes.column 33 * 34 * @augments Highcharts.Series 35 */ 36class ColumnSeries extends Series { 37 /* * 38 * 39 * Functions 40 * 41 * */ 42 /* eslint-disable valid-jsdoc */ 43 /** 44 * Animate the column heights one by one from zero. 45 * 46 * @private 47 * @function Highcharts.seriesTypes.column#animate 48 * 49 * @param {boolean} init 50 * Whether to initialize the animation or run it 51 */ 52 animate(init) { 53 const series = this, yAxis = this.yAxis, yAxisPos = yAxis.pos, reversed = yAxis.reversed, options = series.options, { clipOffset, inverted } = this.chart, attr = {}, translateProp = inverted ? 54 'translateX' : 55 'translateY'; 56 let translateStart, translatedThreshold; 57 if (init && clipOffset) { 58 attr.scaleY = 0.001; 59 translatedThreshold = clamp(yAxis.toPixels(options.threshold || 0), yAxisPos, yAxisPos + yAxis.len); 60 if (inverted) { 61 // Make sure the columns don't cover the axis line during 62 // entrance animation 63 translatedThreshold += reversed ? 64 -Math.floor(clipOffset[0]) : 65 Math.ceil(clipOffset[2]); 66 attr.translateX = translatedThreshold - yAxis.len; 67 } 68 else { 69 // Make sure the columns don't cover the axis line during 70 // entrance animation 71 translatedThreshold += reversed ? 72 Math.ceil(clipOffset[0]) : 73 -Math.floor(clipOffset[2]); 74 attr.translateY = translatedThreshold; 75 } 76 // Apply final clipping (used in Highcharts Stock) (#7083) 77 // animation is done by scaleY, so clipping is for panes 78 if (series.clipBox) { 79 series.setClip(); 80 } 81 series.group.attr(attr); 82 } 83 else { // Run the animation 84 translateStart = Number(series.group.attr(translateProp)); 85 series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), { 86 // Do the scale synchronously to ensure smooth 87 // updating (#5030, #7228) 88 step: function (val, fx) { 89 if (series.group) { 90 attr[translateProp] = translateStart + 91 fx.pos * (yAxisPos - translateStart); 92 series.group.attr(attr); 93 } 94 } 95 })); 96 } 97 } 98 /** 99 * Initialize the series. Extends the basic Series.init method by 100 * marking other series of the same type as dirty. 101 * 102 * @private 103 * @function Highcharts.seriesTypes.column#init 104 */ 105 init(chart, 106 // eslint-disable-next-line @typescript-eslint/no-unused-vars 107 options) { 108 super.init.apply(this, arguments); 109 const series = this; 110 chart = series.chart; 111 // If the series is added dynamically, force redraw of other 112 // series affected by a new column 113 if (chart.hasRendered) { 114 chart.series.forEach(function (otherSeries) { 115 if (otherSeries.type === series.type) { 116 otherSeries.isDirty = true; 117 } 118 }); 119 } 120 } 121 /** 122 * Return the width and x offset of the columns adjusted for grouping, 123 * groupPadding, pointPadding, pointWidth etc. 124 * 125 * @private 126 * @function Highcharts.seriesTypes.column#getColumnMetrics 127 */ 128 getColumnMetrics() { 129 const series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks, 130 // Keep backward compatibility: reversed xAxis had reversed 131 // stacks 132 reverseStacks = (xAxis.reversed && !reversedStacks) || 133 (!xAxis.reversed && reversedStacks), stackGroups = {}; 134 let stackKey, columnCount = 0; 135 // Get the total number of column type series. This is called on 136 // every series. Consider moving this logic to a chart.orderStacks() 137 // function and call it on init, addSeries and removeSeries 138 if (options.grouping === false) { 139 columnCount = 1; 140 } 141 else {
vendor: 10,825 bytes, lines 142-354
142 series.chart.series.forEach(function (otherSeries) { 143 const otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options; 144 let columnIndex; 145 if (otherSeries.type === series.type && 146 otherSeries.reserveSpace() && 147 yAxis.len === otherYAxis.len && 148 yAxis.pos === otherYAxis.pos) { // #642, #2086 149 if (otherOptions.stacking && 150 otherOptions.stacking !== 'group') { 151 stackKey = otherSeries.stackKey; 152 if (typeof stackGroups[stackKey] === 153 'undefined') { 154 stackGroups[stackKey] = columnCount++; 155 } 156 columnIndex = stackGroups[stackKey]; 157 } 158 else if (otherOptions.grouping !== false) { // #1162 159 columnIndex = columnCount++; 160 } 161 otherSeries.columnIndex = columnIndex; 162 } 163 }); 164 } 165 const categoryWidth = Math.min(Math.abs(xAxis.transA) * ((!xAxis.brokenAxis?.hasBreaks && xAxis.ordinal?.slope) || 166 options.pointRange || 167 xAxis.closestPointRange || 168 xAxis.tickInterval || 169 1), // #2610 170 xAxis.len // #1535 171 ), groupPadding = categoryWidth * options.groupPadding, groupWidth = categoryWidth - 2 * groupPadding, pointOffsetWidth = groupWidth / (columnCount || 1), pointWidth = Math.min(options.maxPointWidth || xAxis.len, pick(options.pointWidth, pointOffsetWidth * (1 - 2 * options.pointPadding))), pointPadding = (pointOffsetWidth - pointWidth) / 2, 172 // #1251, #3737 173 colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding + 174 (groupPadding + 175 colIndex * pointOffsetWidth - 176 (categoryWidth / 2)) * (reverseStacks ? -1 : 1); 177 // Save it for reading in linked series (Error bars particularly) 178 series.columnMetrics = { 179 width: pointWidth, 180 offset: pointXOffset, 181 paddedWidth: pointOffsetWidth, 182 columnCount 183 }; 184 return series.columnMetrics; 185 } 186 /** 187 * Make the columns crisp. The edges are rounded to the nearest full 188 * pixel. 189 * 190 * @private 191 * @function Highcharts.seriesTypes.column#crispCol 192 */ 193 crispCol(x, y, width, height) { 194 const borderWidth = this.borderWidth, inverted = this.chart.inverted, bottom = crisp(y + height, borderWidth, inverted); 195 // Vertical 196 y = crisp(y, borderWidth, inverted); 197 height = bottom - y; 198 // Horizontal. We need to first compute the exact right edge, then 199 // round it and compute the width from there. 200 if (this.options.crisp) { 201 const right = crisp(x + width, borderWidth); 202 x = crisp(x, borderWidth); 203 width = right - x; 204 } 205 return { x, y, width, height }; 206 } 207 /** 208 * Adjust for missing columns, according to the `centerInCategory` 209 * option. Missing columns are either single points or stacks where the 210 * point or points are either missing or null. 211 * 212 * @private 213 * @function Highcharts.seriesTypes.column#adjustForMissingColumns 214 * @param {number} x 215 * The x coordinate of the column, left side 216 * 217 * @param {number} pointWidth 218 * The pointWidth, already computed upstream 219 * 220 * @param {Highcharts.ColumnPoint} point 221 * The point instance 222 * 223 * @param {Highcharts.ColumnMetricsObject} metrics 224 * The series-wide column metrics 225 * 226 * @return {number} 227 * The adjusted x position, or the original if not adjusted 228 */ 229 adjustForMissingColumns(x, pointWidth, point, metrics) { 230 if (!point.isNull && metrics.columnCount > 1) { 231 const visibleSeries = this.xAxis.series 232 .filter((s) => s.visible) 233 .map((s) => s.index); 234 let indexInCategory = 0, totalInCategory = 0; 235 // Loop over all the stacks on the Y axis. When stacking is enabled, 236 // these are real point stacks. When stacking is not enabled, but 237 // `centerInCategory` is true, there is one stack handling the 238 // grouping of points in each category. This is done in the 239 // `setGroupedPoints` function. 240 objectEach(this.xAxis.stacking?.stacks, (stack) => { 241 const points = typeof point.x === 'number' ? 242 stack[point.x.toString()]?.points : 243 void 0, pointValues = points?.[this.index], yStackMap = {}; 244 // Look for the index 245 if (points && isArray(pointValues)) { 246 let baseIndex = this.index; 247 // If there are multiple points with the same X then 248 // gather all series in category, and assign index 249 const seriesIndexes = Object 250 .keys(points) 251 .filter((pointKey) => 252 // Filter out duplicate X's 253 !pointKey.match(',') && 254 // Filter out null points 255 points[pointKey] && 256 points[pointKey].length > 1) 257 .map(parseFloat) 258 .filter((index) => visibleSeries.indexOf(index) !== -1) 259 // When the series `stack` option is defined, assign 260 // all subsequent column of the same stack to the 261 // same index as the base column of the stack, then 262 // filter out the original series index so that 263 // `seriesIndexes` is shortened to the amount of 264 // stacks, not the amount of series (#20550). 265 .filter((index) => { 266 const otherOptions = this.chart.series[index] 267 .options, yStack = otherOptions.stacking && 268 otherOptions.stack; 269 if (defined(yStack)) { 270 if (isNumber(yStackMap[yStack])) { 271 if (baseIndex === index) { 272 baseIndex = yStackMap[yStack]; 273 } 274 return false; 275 } 276 yStackMap[yStack] = index; 277 } 278 return true; 279 }) 280 .sort((a, b) => b - a); 281 indexInCategory = seriesIndexes.indexOf(baseIndex); 282 totalInCategory = seriesIndexes.length; 283 } 284 }); 285 indexInCategory = this.xAxis.reversed ? 286 totalInCategory - 1 - indexInCategory : indexInCategory; 287 // Compute the adjusted x position 288 const boxWidth = (totalInCategory - 1) * metrics.paddedWidth + 289 pointWidth; 290 x = (point.plotX || 0) + boxWidth / 2 - pointWidth - 291 indexInCategory * metrics.paddedWidth; 292 } 293 return x; 294 } 295 /** 296 * Translate each point to the plot area coordinate system and find 297 * shape positions 298 * 299 * @private 300 * @function Highcharts.seriesTypes.column#translate 301 */ 302 translate() { 303 const series = this, chart = series.chart, options = series.options, dense = series.dense = 304 series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635 305 ), xAxis = series.xAxis, yAxis = series.yAxis, threshold = options.threshold, minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), seriesPointWidth = metrics.width, seriesXOffset = series.pointXOffset = metrics.offset, dataMin = series.dataMin, dataMax = series.dataMax, translatedThreshold = series.translatedThreshold = 306 yAxis.getThreshold(threshold); 307 // Postprocessed for border width 308 let seriesBarW = series.barW = 309 Math.max(seriesPointWidth, 1 + 2 * borderWidth); 310 // When the pointPadding is 0, we want the columns to be packed 311 // tightly, so we allow individual columns to have individual sizes. 312 // When pointPadding is greater, we strive for equal-width columns 313 // (#2694). 314 if (options.pointPadding && options.crisp) { 315 seriesBarW = Math.ceil(seriesBarW); 316 } 317 Series.prototype.translate.apply(series); 318 // Record the new values 319 series.points.forEach(function (point) { 320 const yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), plotX = point.plotX || 0, 321 // Don't draw too far outside plot area (#1303, #2241, 322 // #4264) 323 plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance); 324 let up, barY = Math.min(plotY, yBottom), barH = Math.max(plotY, yBottom) - barY, pointWidth = seriesPointWidth, barX = plotX + seriesXOffset, barW = seriesBarW; 325 // Handle options.minPointLength 326 if (minPointLength && Math.abs(barH) < minPointLength) { 327 barH = minPointLength; 328 up = (!yAxis.reversed && !point.negative) || 329 (yAxis.reversed && point.negative); 330 // Reverse zeros if there's no positive value in the series 331 // in visible range (#7046) 332 if (isNumber(threshold) && 333 isNumber(dataMax) && 334 point.y === threshold && 335 dataMax <= threshold && 336 // And if there's room for it (#7311) 337 (yAxis.min || 0) < threshold && 338 // If all points are the same value (i.e zero) not draw 339 // as negative points (#10646), but only if there's room 340 // for it (#14876) 341 (dataMin !== dataMax || (yAxis.max || 0) <= threshold)) { 342 up = !up; 343 point.negative = !point.negative; 344 } 345 // If stacked... 346 barY = (Math.abs(barY - translatedThreshold) > minPointLength ? 347 // ...keep position 348 yBottom - minPointLength : 349 // #1485, #4051 350 translatedThreshold - 351 (up ? minPointLength : 0)); 352 } 353 // Handle point.options.pointWidth 354 // @todo Handle grouping/stacking too. Calculate offset properly
355 if (defined(point.options.pointWidth)) { 356 pointWidth = barW = 357 Math.ceil(point.options.pointWidth); 358 barX -= Math.round((pointWidth - seriesPointWidth) / 2); 359 } 360 // Adjust for null or missing points 361 if (options.centerInCategory) { 362 barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics); 363 } 364 // Cache for access in polar 365 point.barX = barX; 366 point.pointWidth = pointWidth; 367 // Fix the tooltip on center of grouped columns (#1216, #424, 368 // #3648) 369 point.tooltipPos = chart.inverted ? 370 [ 371 clamp(yAxis.len + yAxis.pos - chart.plotLeft - plotY, yAxis.pos - chart.plotLeft, yAxis.len + yAxis.pos - chart.plotLeft), 372 xAxis.len + xAxis.pos - chart.plotTop - barX - barW / 2, 373 barH 374 ] : 375 [ 376 xAxis.left - chart.plotLeft + barX + barW / 2, 377 clamp(plotY + yAxis.pos - 378 chart.plotTop, yAxis.pos - chart.plotTop, yAxis.len + yAxis.pos - chart.plotTop), 379 barH 380 ]; 381 // Register shape type and arguments to be used in drawPoints. Allow 382 // `shapeType` defined on `pointClass` level. 383 point.shapeType = series.pointClass.prototype.shapeType || 384 'roundedRect'; 385 point.shapeArgs = series.crispCol(barX, 386 // #3169, drilldown from null must have a position to work from. 387 // #6585, dataLabel should be placed on xAxis, not floating in 388 // the middle of the chart. 389 point.isNull ? translatedThreshold : barY, barW, point.isNull ? 0 : barH); 390 }); 391 // Fire a specific event after column translate. We could instead apply 392 // all the column logic in an `afterTranslate` event handler, but there 393 // are so many other series types that use the column translation, that 394 // it is more convenient to have a specific event for it. 395 fireEvent(this, 'afterColumnTranslate'); 396 } 397 /** 398 * Columns have no graph 399 * 400 * @private 401 * @function Highcharts.seriesTypes.column#drawGraph 402 */ 403 drawGraph() { 404 this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data'); 405 } 406 /** 407 * Get presentational attributes 408 * 409 * @private 410 * @function Highcharts.seriesTypes.column#pointAttribs 411 */ 412 pointAttribs(point, state) { 413 const options = this.options, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth'; 414 let stateOptions, zone, brightness, fill = (point && point.color) || this.color, 415 // Set to fill when borderColor null: 416 stroke = ((point && point[strokeOption]) || 417 options[strokeOption] || 418 fill), dashstyle = (point && point.options.dashStyle) || options.dashStyle, strokeWidth = (point && point[strokeWidthOption]) || 419 options[strokeWidthOption] || 420 this[strokeWidthOption] || 0, opacity = pick(point && point.opacity, options.opacity, 1); 421 // Handle zone colors 422 if (point && this.zones.length) { 423 zone = point.getZone(); 424 // When zones are present, don't use point.color (#4267). 425 // Changed order (#6527), added support for colorAxis (#10670) 426 fill = (point.options.color || 427 (zone && (zone.color || point.nonZonedColor)) || 428 this.color); 429 if (zone) { 430 stroke = zone.borderColor || stroke; 431 dashstyle = zone.dashStyle || dashstyle; 432 strokeWidth = zone.borderWidth || strokeWidth; 433 } 434 } 435 // Select or hover states 436 if (state && point) { 437 stateOptions = merge(options.states[state], 438 // #6401 439 point.options.states && 440 point.options.states[state] || 441 {}); 442 brightness = stateOptions.brightness; 443 fill = 444 stateOptions.color || (typeof brightness !== 'undefined' && 445 color(fill) 446 .brighten(stateOptions.brightness) 447 .get()) || fill; 448 stroke = stateOptions[strokeOption] || stroke; 449 strokeWidth = 450 stateOptions[strokeWidthOption] || strokeWidth; 451 dashstyle = stateOptions.dashStyle || dashstyle; 452 opacity = pick(stateOptions.opacity, opacity); 453 } 454 const ret = { 455 fill: fill, 456 stroke: stroke, 457 'stroke-width': strokeWidth, 458 opacity: opacity 459 }; 460 if (dashstyle) { 461 ret.dashstyle = dashstyle; 462 } 463 return ret; 464 } 465 /** 466 * Draw the columns. For bars, the series.group is rotated, so the same 467 * coordinates apply for columns and bars. This method is inherited by 468 * scatter series. 469 * 470 * @private 471 * @function Highcharts.seriesTypes.column#drawPoints 472 */ 473 drawPoints(points = this.points) { 474 const series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250; 475 let shapeArgs; 476 // Draw the columns
477 points.forEach(function (point) { 478 const plotY = point.plotY; 479 let graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ? 480 'animate' : 'attr'; 481 if (isNumber(plotY) && point.y !== null) { 482 shapeArgs = point.shapeArgs; 483 // When updating a series between 2d and 3d or cartesian and 484 // polar, the shape type changes. 485 if (graphic && point.hasNewShapeType()) { 486 graphic = graphic.destroy(); 487 } 488 // Set starting position for point sliding animation. 489 if (series.enabledDataSorting) { 490 point.startXPos = series.xAxis.reversed ? 491 -(shapeArgs ? (shapeArgs.width || 0) : 0) : 492 series.xAxis.width; 493 } 494 if (!graphic) { 495 point.graphic = graphic = 496 renderer[point.shapeType](shapeArgs) 497 .add(point.group || series.group); 498 if (graphic && 499 series.enabledDataSorting && 500 chart.hasRendered && 501 chart.pointCount < animationLimit) { 502 graphic.attr({ 503 x: point.startXPos 504 }); 505 hasGraphic = true; 506 verb = 'animate'; 507 } 508 } 509 if (graphic && hasGraphic) { // Update 510 graphic[verb](merge(shapeArgs)); 511 } 512 // Presentational 513 if (!chart.styledMode) { 514 graphic[verb](series.pointAttribs(point, (point.selected && 'select'))) 515 .shadow(point.allowShadow !== false && options.shadow); 516 } 517 if (graphic) { 518 graphic.addClass(point.getClassName(), true); 519 graphic.attr({ 520 visibility: point.visible ? 'inherit' : 'hidden' 521 }); 522 } 523 } 524 else if (graphic) { 525 point.graphic = graphic.destroy(); // #1269 526 } 527 }); 528 } 529 /** 530 * Draw the tracker for a point. 531 * @private 532 */ 533 drawTracker(points = this.points) { 534 const series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) { 535 pointer?.normalize(e); 536 const point = pointer?.getPointFromEvent(e), 537 // Run point events only for points inside plot area, #21136 538 isInsidePlot = chart.scrollablePlotArea ? 539 chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop, { 540 visiblePlotOnly: true 541 }) : true; 542 // Undefined on graph in scatterchart 543 if (pointer && 544 point && 545 series.options.enableMouseTracking && 546 isInsidePlot) { 547 pointer.isDirectTouch = true; 548 point.onMouseOver(e); 549 } 550 }; 551 let dataLabels; 552 // Add reference to the point 553 points.forEach(function (point) { 554 dataLabels = (isArray(point.dataLabels) ? 555 point.dataLabels : 556 (point.dataLabel ? [point.dataLabel] : [])); 557 if (point.graphic) { 558 point.graphic.element.point = point; 559 }
560 dataLabels.forEach(function (dataLabel) { 561 (dataLabel.div || dataLabel.element).point = point; 562 }); 563 }); 564 // Add the event listeners, we need to do this only once 565 if (!series._hasTracking) { 566 series.trackerGroups.forEach(function (key) { 567 if (series[key]) { 568 // We don't always have dataLabelsGroup 569 series[key] 570 .addClass('highcharts-tracker') 571 .on('mouseover', onMouseOver) 572 .on('mouseout', function (e) { 573 pointer?.onTrackerMouseOut(e); 574 }) 575 .on('touchstart', onMouseOver); 576 if (!chart.styledMode && series.options.cursor) { 577 series[key] 578 .css({ cursor: series.options.cursor }); 579 } 580 } 581 }); 582 series._hasTracking = true; 583 } 584 fireEvent(this, 'afterDrawTracker'); 585 } 586 /** 587 * Remove this series from the chart 588 * 589 * @private 590 * @function Highcharts.seriesTypes.column#remove 591 */ 592 remove() { 593 const series = this, chart = series.chart; 594 // Column and bar series affects other series of the same type 595 // as they are either stacked or grouped 596 if (chart.hasRendered) { 597 chart.series.forEach(function (otherSeries) { 598 if (otherSeries.type === series.type) { 599 otherSeries.isDirty = true; 600 } 601 }); 602 } 603 Series.prototype.remove.apply(series, arguments); 604 } 605} 606/* * 607 * 608 * Static Properties 609 * 610 * */ 611ColumnSeries.defaultOptions = merge(Series.defaultOptions, ColumnSeriesDefaults); 612extend(ColumnSeries.prototype, { 613 // When tooltip is not shared, this series (and derivatives) requires 614 // direct touch/hover. KD-tree does not apply. 615 directTouch: true, 616 getSymbol: noop, 617 // Use separate negative stacks, unlike area stacks where a negative 618 // point is subtracted from previous (#1910) 619 negStacks: true, 620 trackerGroups: ['group', 'dataLabelsGroup'] 621}); 622SeriesRegistry.registerSeriesType('column', ColumnSeries); 623/* * 624 * 625 * Default Export 626 * 627 * */ 628export default ColumnSeries; 629/* * 630 * 631 * API Declarations 632 * 633 * */ 634/** 635 * Adjusted width and x offset of the columns for grouping. 636 * 637 * @private 638 * @interface Highcharts.ColumnMetricsObject 639 */ /** 640* Width of the columns. 641* @name Highcharts.ColumnMetricsObject#width 642* @type {number} 643*/ /** 644* Offset of the columns. 645* @name Highcharts.ColumnMetricsObject#offset 646* @type {number} 647*/ 648''; // Detach doclets above
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.