| MEASURE_DIMENSIONS_V1 = """ | |
| () => { | |
| const body = document.body; | |
| const html = document.documentElement; | |
| // Force layout calculation | |
| body.offsetHeight; | |
| // Get body's computed style to extract margins | |
| const bodyStyle = window.getComputedStyle(body); | |
| const marginTop = parseFloat(bodyStyle.marginTop) || 0; | |
| const marginBottom = parseFloat(bodyStyle.marginBottom) || 0; | |
| const marginLeft = parseFloat(bodyStyle.marginLeft) || 0; | |
| const marginRight = parseFloat(bodyStyle.marginRight) || 0; | |
| const bodyRect = body.getBoundingClientRect(); | |
| // Find the furthest extent of content | |
| let maxY = bodyRect.bottom; | |
| let maxX = bodyRect.right; | |
| const allElements = body.querySelectorAll('*'); | |
| allElements.forEach(el => { | |
| const rect = el.getBoundingClientRect(); | |
| const style = window.getComputedStyle(el); | |
| if (style.display === 'none' || | |
| style.visibility === 'hidden' || | |
| rect.width === 0 || rect.height === 0) { | |
| return; | |
| } | |
| if (rect.bottom > maxY) maxY = rect.bottom; | |
| if (rect.right > maxX) maxX = rect.right; | |
| }); | |
| // CRITICAL FIX: Include body margins in the total size | |
| // The PDF needs to be tall enough to contain the margins too! | |
| const totalWidth = Math.ceil(maxX - bodyRect.left + marginRight + 5); | |
| const totalHeight = Math.ceil(maxY + marginBottom + 5); | |
| return { | |
| width: totalWidth, | |
| height: totalHeight, | |
| debug: { | |
| marginTop, | |
| marginBottom, | |
| marginLeft, | |
| marginRight, | |
| bodyRectTop: bodyRect.top, | |
| bodyRectBottom: bodyRect.bottom, | |
| maxY, | |
| contentHeightWithoutMargin: Math.ceil(maxY - bodyRect.top) | |
| } | |
| }; | |
| } | |
| """ | |
| MEASURE_DIMENSIONS_V2 = """ | |
| () => { | |
| const body = document.body; | |
| const html = document.documentElement; | |
| // Force layout calculation | |
| body.offsetHeight; | |
| html.offsetHeight; | |
| // Get body's computed style to extract margins | |
| const bodyStyle = window.getComputedStyle(body); | |
| const marginTop = parseFloat(bodyStyle.marginTop) || 0; | |
| const marginBottom = parseFloat(bodyStyle.marginBottom) || 0; | |
| const marginLeft = parseFloat(bodyStyle.marginLeft) || 0; | |
| const marginRight = parseFloat(bodyStyle.marginRight) || 0; | |
| // Strategy: Find the bounding box of ALL visible content | |
| // This works for narrow receipts, wide tables, multi-column, everything | |
| let minX = Infinity; | |
| let minY = Infinity; | |
| let maxX = -Infinity; | |
| let maxY = -Infinity; | |
| // Check body itself | |
| const bodyRect = body.getBoundingClientRect(); | |
| if (bodyRect.width > 0 && bodyRect.height > 0) { | |
| minX = Math.min(minX, bodyRect.left); | |
| minY = Math.min(minY, bodyRect.top); | |
| maxX = Math.max(maxX, bodyRect.right); | |
| maxY = Math.max(maxY, bodyRect.bottom); | |
| } | |
| // Check all elements to find true content bounds | |
| const allElements = document.querySelectorAll('*'); | |
| allElements.forEach(el => { | |
| const rect = el.getBoundingClientRect(); | |
| const style = window.getComputedStyle(el); | |
| // Skip hidden elements | |
| if (style.display === 'none' || | |
| style.visibility === 'hidden' || | |
| rect.width === 0 || | |
| rect.height === 0) { | |
| return; | |
| } | |
| // Skip script/style tags | |
| if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE') { | |
| return; | |
| } | |
| minX = Math.min(minX, rect.left); | |
| minY = Math.min(minY, rect.top); | |
| maxX = Math.max(maxX, rect.right); | |
| maxY = Math.max(maxY, rect.bottom); | |
| }); | |
| // Fallback if no content found | |
| if (minX === Infinity) { | |
| minX = 0; | |
| minY = 0; | |
| maxX = bodyRect.right; | |
| maxY = bodyRect.bottom; | |
| } | |
| // Calculate total dimensions | |
| // Width: from leftmost to rightmost content + right margin | |
| // Height: from topmost to bottommost content + bottom margin | |
| const buffer = 5; // Small safety buffer | |
| const totalWidth = Math.ceil(maxX - minX + marginRight + buffer); | |
| const totalHeight = Math.ceil(maxY - minY + marginBottom + buffer); | |
| return { | |
| width: totalWidth, | |
| height: totalHeight, | |
| debug: { | |
| marginTop, | |
| marginBottom, | |
| marginLeft, | |
| marginRight, | |
| minX, | |
| minY, | |
| maxX, | |
| maxY, | |
| bodyWidth: bodyRect.width, | |
| bodyHeight: bodyRect.height | |
| } | |
| }; | |
| } | |
| """ | |
| MEASURE_DIMENSIONS_V3 = """ | |
| () => { | |
| const body = document.body; | |
| // Force layout | |
| body.offsetHeight; | |
| const bodyRect = body.getBoundingClientRect(); | |
| // For receipts/documents with body padding, the body rect already includes everything | |
| // Just add a small buffer | |
| const buffer = 5; | |
| return { | |
| width: Math.ceil(bodyRect.width + buffer), | |
| height: Math.ceil(bodyRect.height + buffer) | |
| }; | |
| } | |
| """ | |
| MEASURE_DIMENSIONS_V4 = """ | |
| () => { | |
| const body = document.body; | |
| const html = document.documentElement; | |
| // Force layout calculation | |
| body.offsetHeight; | |
| html.offsetHeight; | |
| // Get body's computed style to extract margins | |
| const bodyStyle = window.getComputedStyle(body); | |
| const marginTop = parseFloat(bodyStyle.marginTop) || 0; | |
| const marginBottom = parseFloat(bodyStyle.marginBottom) || 0; | |
| const marginLeft = parseFloat(bodyStyle.marginLeft) || 0; | |
| const marginRight = parseFloat(bodyStyle.marginRight) || 0; | |
| // Get body padding as well | |
| const paddingTop = parseFloat(bodyStyle.paddingTop) || 0; | |
| const paddingBottom = parseFloat(bodyStyle.paddingBottom) || 0; | |
| const paddingLeft = parseFloat(bodyStyle.paddingLeft) || 0; | |
| const paddingRight = parseFloat(bodyStyle.paddingRight) || 0; | |
| // Strategy: Find the bounding box of ALL visible content | |
| let minX = Infinity; | |
| let minY = Infinity; | |
| let maxX = -Infinity; | |
| let maxY = -Infinity; | |
| // Check body itself | |
| const bodyRect = body.getBoundingClientRect(); | |
| if (bodyRect.width > 0 && bodyRect.height > 0) { | |
| minX = Math.min(minX, bodyRect.left); | |
| minY = Math.min(minY, bodyRect.top); | |
| maxX = Math.max(maxX, bodyRect.right); | |
| maxY = Math.max(maxY, bodyRect.bottom); | |
| } | |
| // Check all elements to find true content bounds | |
| const allElements = document.querySelectorAll('*'); | |
| allElements.forEach(el => { | |
| const rect = el.getBoundingClientRect(); | |
| const style = window.getComputedStyle(el); | |
| // Skip hidden elements | |
| if (style.display === 'none' || | |
| style.visibility === 'hidden' || | |
| rect.width === 0 || | |
| rect.height === 0) { | |
| return; | |
| } | |
| // Skip script/style tags | |
| if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE') { | |
| return; | |
| } | |
| minX = Math.min(minX, rect.left); | |
| minY = Math.min(minY, rect.top); | |
| maxX = Math.max(maxX, rect.right); | |
| maxY = Math.max(maxY, rect.bottom); | |
| }); | |
| // Fallback if no content found | |
| if (minX === Infinity) { | |
| minX = 0; | |
| minY = 0; | |
| maxX = bodyRect.right; | |
| maxY = bodyRect.bottom; | |
| } | |
| // Calculate total dimensions | |
| // CRITICAL FIX: The viewport starts at 0,0 but content might be offset | |
| // We need the full document size, not just content span | |
| // For width: take the maximum of either the rightmost content or body width | |
| // For height: take the maximum of either the bottommost content or body height | |
| const buffer = 5; | |
| // Option A: Measure from viewport origin (0,0) to furthest content | |
| const totalWidth = Math.ceil(maxX + buffer); | |
| const totalHeight = Math.ceil(maxY + buffer); | |
| // Option B: Also consider body's full width (in case body is wider than content) | |
| const bodyFullWidth = bodyRect.width; | |
| const bodyFullHeight = bodyRect.height; | |
| // Use whichever is larger | |
| const finalWidth = Math.max(totalWidth, bodyFullWidth); | |
| const finalHeight = Math.max(totalHeight, bodyFullHeight); | |
| return { | |
| width: finalWidth, | |
| height: finalHeight, | |
| debug: { | |
| marginTop, | |
| marginBottom, | |
| marginLeft, | |
| marginRight, | |
| paddingTop, | |
| paddingBottom, | |
| paddingLeft, | |
| paddingRight, | |
| minX, | |
| minY, | |
| maxX, | |
| maxY, | |
| bodyWidth: bodyRect.width, | |
| bodyHeight: bodyRect.height, | |
| bodyLeft: bodyRect.left, | |
| bodyTop: bodyRect.top, | |
| totalWidth, | |
| totalHeight, | |
| bodyFullWidth, | |
| bodyFullHeight | |
| } | |
| }; | |
| } | |
| """ | |
| MEASURE_DIMENSIONS = """ | |
| () => { | |
| const body = document.body; | |
| const html = document.documentElement; | |
| // Force layout | |
| body.offsetHeight; | |
| html.offsetHeight; | |
| const bodyStyle = window.getComputedStyle(body); | |
| const paddingTop = parseFloat(bodyStyle.paddingTop) || 0; | |
| const paddingBottom = parseFloat(bodyStyle.paddingBottom) || 0; | |
| const paddingLeft = parseFloat(bodyStyle.paddingLeft) || 0; | |
| const paddingRight = parseFloat(bodyStyle.paddingRight) || 0; | |
| // Strategy: Find bounding box of ALL visible content | |
| let minX = Infinity; | |
| let minY = Infinity; | |
| let maxX = -Infinity; | |
| let maxY = -Infinity; | |
| const bodyRect = body.getBoundingClientRect(); | |
| // Check all elements (not just body children, in case of deep nesting) | |
| const allElements = document.querySelectorAll('body *'); | |
| let hasContent = false; | |
| allElements.forEach(el => { | |
| // Skip scripts, styles, and hidden elements | |
| if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE') return; | |
| const rect = el.getBoundingClientRect(); | |
| const style = window.getComputedStyle(el); | |
| if (style.display === 'none' || style.visibility === 'hidden' || | |
| rect.width === 0 || rect.height === 0) { | |
| return; | |
| } | |
| hasContent = true; | |
| minX = Math.min(minX, rect.left); | |
| minY = Math.min(minY, rect.top); | |
| maxX = Math.max(maxX, rect.right); | |
| maxY = Math.max(maxY, rect.bottom); | |
| }); | |
| // Fallback if no content found | |
| if (!hasContent || minX === Infinity) { | |
| return { | |
| width: Math.ceil(bodyRect.width + 5), | |
| height: Math.ceil(bodyRect.height + 5) | |
| }; | |
| } | |
| // Now decide: do we measure from content bounds or from body bounds? | |
| // Approach 1: Content-based (for narrow receipts) | |
| // Width = actual content span + left padding + right padding | |
| const contentWidth = maxX - minX; | |
| const contentHeight = maxY - minY; | |
| const contentBasedWidth = contentWidth + paddingLeft + paddingRight; | |
| const contentBasedHeight = contentHeight + paddingTop + paddingBottom; | |
| // Approach 2: Body-based (for full-width documents) | |
| // Width = body's full width | |
| const bodyBasedWidth = bodyRect.width; | |
| const bodyBasedHeight = bodyRect.height; | |
| // Decision logic: | |
| // If content is significantly narrower than body (e.g., < 70% of body width), | |
| // it's likely a centered narrow layout like a receipt | |
| // Otherwise, it's a full-width document | |
| const contentWidthRatio = contentWidth / bodyRect.width; | |
| const isNarrowCentered = contentWidthRatio < 0.7; | |
| let finalWidth, finalHeight; | |
| if (isNarrowCentered) { | |
| // Use content-based measurement (receipt-style) | |
| finalWidth = contentBasedWidth; | |
| finalHeight = Math.max(contentBasedHeight, bodyBasedHeight); // Use max for height | |
| } else { | |
| // Use body-based measurement (full-width document) | |
| finalWidth = bodyBasedWidth; | |
| finalHeight = bodyBasedHeight; | |
| } | |
| const buffer = 5; | |
| return { | |
| width: Math.ceil(finalWidth + buffer), | |
| height: Math.ceil(finalHeight + buffer), | |
| debug: { | |
| isNarrowCentered, | |
| contentWidthRatio: contentWidthRatio.toFixed(2), | |
| contentWidth, | |
| contentHeight, | |
| bodyWidth: bodyRect.width, | |
| bodyHeight: bodyRect.height, | |
| approach: isNarrowCentered ? 'content-based' : 'body-based' | |
| } | |
| }; | |
| } | |
| """ | |