Created
December 2, 2024 14:14
-
-
Save mopsicus/120f2e808cb22801a5c0ba2758775af1 to your computer and use it in GitHub Desktop.
Russian Lotto card generator. Generate any number of pages with 4 cards to play.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- This code generated via ChatGPT by Mopsicus --> | |
| <!DOCTYPE html> | |
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Генератор карточек для Русского Лото</title> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| text-align: center; | |
| padding: 20px; | |
| } | |
| #controls { | |
| margin-bottom: 20px; | |
| } | |
| input[type="number"] { | |
| padding: 5px; | |
| width: 100px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Генератор карточек для Русского Лото</h1> | |
| <div id="controls"> | |
| <label for="pageCount">Количество страниц: </label> | |
| <input type="number" id="pageCount" min="1" value="1"> | |
| <button onclick="generatePDF()">Сгенерировать PDF</button> | |
| </div> | |
| <div id="cardContainer"> | |
| <!-- Генерация карточек будет отображаться здесь --> | |
| </div> | |
| <script> | |
| // Функция для генерации одной карточки | |
| const generateLotoCard = () => { | |
| const card = [ | |
| Array(9).fill(0), // Первая строка | |
| Array(9).fill(0), // Вторая строка | |
| Array(9).fill(0), // Третья строка | |
| ]; | |
| const columnRanges = [ | |
| [1, 9], | |
| [10, 19], | |
| [20, 29], | |
| [30, 39], | |
| [40, 49], | |
| [50, 59], | |
| [60, 69], | |
| [70, 79], | |
| [80, 90] | |
| ]; | |
| const generateValidCard = () => { | |
| const tempCard = [ | |
| Array(9).fill(0), | |
| Array(9).fill(0), | |
| Array(9).fill(0), | |
| ]; | |
| for (let col = 0; col < 9; col++) { | |
| const [min, max] = columnRanges[col]; | |
| const numbersInColumn = Array.from({ length: max - min + 1 }, (_, i) => i + min); | |
| const numCount = Math.floor(Math.random() * 2) + 1; | |
| const selectedNumbers = []; | |
| for (let i = 0; i < numCount; i++) { | |
| const randomIndex = Math.floor(Math.random() * numbersInColumn.length); | |
| const num = numbersInColumn[randomIndex]; | |
| selectedNumbers.push(num); | |
| numbersInColumn.splice(randomIndex, 1); | |
| } | |
| for (const num of selectedNumbers) { | |
| let row; | |
| do { | |
| row = Math.floor(Math.random() * 3); | |
| } while (tempCard[row][col] !== 0); | |
| tempCard[row][col] = num; | |
| } | |
| } | |
| const isValid = tempCard.every(row => row.filter(num => num !== 0).length === 5); | |
| if (isValid) { | |
| return tempCard; | |
| } else { | |
| return generateValidCard(); | |
| } | |
| }; | |
| return generateValidCard(); | |
| }; | |
| // Генерация PDF с заданным количеством страниц | |
| const generatePDF = async () => { | |
| const pageCount = parseInt(document.getElementById('pageCount').value); | |
| const { PDFDocument } = PDFLib; | |
| const pdfDoc = await PDFDocument.create(); | |
| const pageWidth = 595; // Размер страницы A4 в точках | |
| const pageHeight = 842; | |
| // Встраиваем шрифт Helvetica | |
| const font = await pdfDoc.embedFont(PDFLib.StandardFonts.Helvetica); | |
| const fontSize = 20; // Увеличенный размер шрифта для чисел | |
| // Цвет для текста | |
| const blackColor = PDFLib.rgb(0, 0, 0); | |
| // Расстояние между карточками | |
| const verticalSpacing = 20; // Увеличенное расстояние между карточками по вертикали | |
| for (let i = 0; i < pageCount; i++) { | |
| const page = pdfDoc.addPage([pageWidth, pageHeight]); | |
| // Вставка надписи, строго по центру | |
| const title = 'LOTO'; | |
| const titleWidth = font.widthOfTextAtSize(title, 20); | |
| page.drawText(title, { | |
| x: (pageWidth - titleWidth) / 2, // Центрирование по X | |
| y: pageHeight - 50, | |
| size: 20, | |
| font, | |
| color: blackColor, | |
| }); | |
| // Генерация и отрисовка карточек | |
| const cardWidth = pageWidth - 20; // Карточки занимают всю ширину | |
| const cardHeight = 150; // Высота карточки | |
| const cardsPerColumn = Math.floor((pageHeight - 100) / (cardHeight + verticalSpacing)); // Количество карточек по вертикали | |
| for (let row = 0; row < cardsPerColumn; row++) { | |
| const card = generateLotoCard(); | |
| const cardY = pageHeight - (row + 1) * (cardHeight + verticalSpacing) - 70; | |
| const cardX = 10; // Расположение карточки по горизонтали | |
| // Рисуем рамку карточки (толщина рамки 3 пикселя) | |
| page.drawRectangle({ | |
| x: cardX, | |
| y: cardY, | |
| width: cardWidth, | |
| height: cardHeight, | |
| borderColor: blackColor, | |
| borderWidth: 3, // Устанавливаем толщину рамки 3 пикселя | |
| }); | |
| // Рисуем числа в карточке | |
| const cellWidth = cardWidth / 9; | |
| const cellHeight = cardHeight / 3; | |
| for (let rowIndex = 0; rowIndex < 3; rowIndex++) { | |
| for (let colIndex = 0; colIndex < 9; colIndex++) { | |
| const num = card[rowIndex][colIndex]; | |
| const x = cardX + colIndex * cellWidth + (cellWidth / 2) - 12; // Центрирование по X | |
| const y = cardY + (2 - rowIndex) * cellHeight + (cellHeight / 2) - 10; // Центрирование по Y | |
| // Рисуем рамку для числа (включая пустые ячейки) | |
| page.drawRectangle({ | |
| x: cardX + colIndex * cellWidth, | |
| y: cardY + (2 - rowIndex) * cellHeight, | |
| width: cellWidth, | |
| height: cellHeight, | |
| borderColor: blackColor, | |
| borderWidth: 1, | |
| }); | |
| // Если число присутствует, отображаем его в центре ячейки | |
| if (num !== 0) { | |
| page.drawText(num.toString(), { | |
| x, | |
| y, | |
| size: fontSize, // Увеличенный размер шрифта | |
| font, | |
| color: blackColor, | |
| align: 'center', | |
| baseline: 'middle', | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const pdfBytes = await pdfDoc.save(); | |
| // Создаем ссылку для скачивания | |
| const link = document.createElement('a'); | |
| link.href = URL.createObjectURL(new Blob([pdfBytes], { type: 'application/pdf' })); | |
| link.download = 'loto_cards.pdf'; | |
| link.click(); | |
| }; | |
| </script> | |
| </body> | |
| </html> |
но фалй работает)
Author
Только что проверил в сафари и яндекс браузере, работает, пдфка сохраняется
пишу первый раз сильно не пинайте, немного поковырял код через нейронку яндекса и вроде получилось что то
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Генератор карточек для Русского Лотто</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 20px;
margin: 0;
}
#controls {
margin-bottom: 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
max-width: 600px;
justify-content: flex-start;
}
label {
font-weight: bold;
white-space: nowrap;
min-width: 200px;
text-align: left;
}
input[type="number"] {
padding: 5px;
width: 80px;
}
button {
margin-top: 15px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>Генератор карточек для Русского Лотто</h1>
<div id="controls">
<div class="control-group">
<label for="pageCount">Количество страниц кратно 3м (на странице по 4 карточки):</label>
<input type="number" id="pageCount" min="1" value="6">
</div>
<div class="control-group">
<label for="fontFamily">Шрифт чисел:</label>
<select id="fontFamily">
<option value="Helvetica" selected>Helvetica (обычный)</option>
<option value="HelveticaBold">Helvetica Bold (жирный)</option>
<option value="Helvetica-Oblique">Helvetica Oblique (курсив)</option>
<option value="Helvetica-BoldOblique">Helvetica Bold Oblique</option>
</select>
</div>
<div class="control-group">
<label for="fontSize">Размер шрифта чисел (pt):</label>
<input type="number" id="fontSize" min="16" max="36" value="30">
</div>
<div class="control-group">
<label for="outerBorder">Толщина внешней рамки (px):</label>
<input type="number" id="outerBorder" min="1" max="10" value="4">
</div>
<div class="control-group">
<label for="innerBorder">Толщина внутренних линий (px):</label>
<input type="number" id="innerBorder" min="1" max="5" value="2">
</div>
<!-- Ввод ширины в десятых мм -->
<div class="control-group">
<label for="cardWidthTenths">Ширина карточки (десятые мм) стандарт 2200:</label>
<input type="number" id="cardWidthTenths" min="1060" max="2120" value="1965">
</div>
<!-- Ввод высоты в десятых мм -->
<div class="control-group">
<label for="cardHeightTenths">Высота карточки (десятые мм) стандарт 800:</label>
<input type="number" id="cardHeightTenths" min="350" max="1060" value="657">
</div>
<div class="control-group">
<label for="verticalSpacing">Расстояние между карточками (pt):</label>
<input type="number" id="verticalSpacing" min="7" max="150" value="20">
</div>
<!-- Размер шрифта метки -->
<div class="control-group">
<label for="dateTimeFontSize">Размер шрифта даты‑времени (pt):</label>
<input type="number" id="dateTimeFontSize" min="1" max="20" value="3">
</div>
<div class="control-group">
<label for="numberFontSize">Размер шрифта номера (pt):</label>
<input type="number" id="numberFontSize" min="1" max="20" value="4">
</div>
<!-- Расстояние метки от рамки -->
<div class="control-group">
<label for="footerMargin">Расстояние метки от рамки (pt):</label>
<input type="number" id="footerMargin" min="-50" max="50" value="5">
</div>
<button onclick="generatePDF()">Сгенерировать PDF</button>
</div>
<div id="cardContainer">
<!-- Карточки будут отображаться здесь -->
</div>
<script>
// Генерация карточки лото (9×3, 15 чисел)
const generateLotoCard = () => {
const card = [Array(9).fill(0), Array(9).fill(0), Array(9).fill(0)];
const columnRanges = [
[1, 9], [10, 19], [20, 29], [30, 39], [40, 49],
[50, 59], [60, 69], [70, 79], [80, 90]
];
const generateValidCard = () => {
const tempCard = [Array(9).fill(0), Array(9).fill(0), Array(9).fill(0)];
for (let col = 0; col < 9; col++) {
const [min, max] = columnRanges[col];
const numbersInColumn = Array.from(
{ length: max - min + 1 },
(_, i) => i + min
);
const numCount = Math.floor(Math.random() * 2) + 1;
const selectedNumbers = [];
for (let i = 0; i < numCount; i++) {
const randomIndex = Math.floor(Math.random() * numbersInColumn.length);
const num = numbersInColumn[randomIndex];
selectedNumbers.push(num);
numbersInColumn.splice(randomIndex, 1);
}
for (const num of selectedNumbers) {
let row;
do {
row = Math.floor(Math.random() * 3);
} while (tempCard[row][col] !== 0);
tempCard[row][col] = num;
}
}
const isValid = tempCard.every(row => row.filter(n => n !== 0).length === 5);
return isValid ? tempCard : generateValidCard();
};
return generateValidCard();
};
// Генерация PDF
const generatePDF = async () => {
try {
const pageCount = parseInt(document.getElementById('pageCount').value);
const fontSize = parseInt(document.getElementById('fontSize').value);
const outerBorder = parseInt(document.getElementById('outerBorder').value);
const innerBorder = parseInt(document.getElementById('innerBorder').value);
// Конвертация: десятые мм → pt (1 десятая мм = 0.283464567 pt)
const tenthsToPt = 0.283464567;
// Расчёт высоты карточки в pt
const cardHeightInput = parseInt(document.getElementById('cardHeightTenths').value);
const cardHeight = Math.round(cardHeightInput * tenthsToPt);
// Расчёт ширины карточки в pt
const cardWidthInput = parseInt(document.getElementById('cardWidthTenths').value);
const cardWidth = Math.round(cardWidthInput * tenthsToPt);
const verticalSpacing = parseInt(document.getElementById('verticalSpacing').value);
// Размеры шрифтов для метки
const dateTimeFontSize = parseInt(document.getElementById('dateTimeFontSize').value);
const numberFontSize = parseInt(document.getElementById('numberFontSize').value);
// Расстояние от нижней рамки карточки до метки (в pt)
const footerMargin = parseInt(document.getElementById('footerMargin').value);
const { PDFDocument } = PDFLib;
const pdfDoc = await PDFDocument.create();
const pageWidth = 595; // A4 ширина в pt
const pageHeight = 842; // A4 высота в pt
// Считываем выбранный шрифт (как раньше)
const fontFamily = document.getElementById('fontFamily').value;
let font;
switch (fontFamily) {
case 'Helvetica':
font = await pdfDoc.embedFont(PDFLib.StandardFonts.Helvetica);
break;
case 'HelveticaBold':
font = await pdfDoc.embedFont(PDFLib.StandardFonts.HelveticaBold);
break;
case 'Helvetica-Oblique':
font = await pdfDoc.embedFont(PDFLib.StandardFonts['Helvetica-Oblique']);
break;
case 'Helvetica-BoldOblique':
font = await pdfDoc.embedFont(PDFLib.StandardFonts['Helvetica-BoldOblique']);
break;
default:
font = await pdfDoc.embedFont(PDFLib.StandardFonts.Helvetica);
}
const blackColor = PDFLib.rgb(0, 0, 0);
// !!! ВАЖНО: счётчик теперь ВНЕ цикла по страницам
let globalCardCounter = 1;
for (let i = 0; i < pageCount; i++) {
const page = pdfDoc.addPage([pageWidth, pageHeight]);
// Расчёт количества карточек на странице по вертикали
const cardsPerColumn = Math.floor((pageHeight - 0) / (cardHeight + verticalSpacing));
for (let row = 0; row < cardsPerColumn; row++) {
const card = generateLotoCard();
const cardY = pageHeight - (row + 1) * (cardHeight + verticalSpacing) - 0;
const cardX = (pageWidth - cardWidth) / 2;
// Внешняя рамка карточки (как раньше)
page.drawRectangle({
x: cardX,
y: cardY,
width: cardWidth,
height: cardHeight,
borderColor: blackColor,
borderWidth: outerBorder,
});
const cellWidth = cardWidth / 9;
const cellHeight = cardHeight / 3;
// Рисуем ячейки и числа (как раньше)
for (let rowIndex = 0; rowIndex < 3; rowIndex++) {
for (let colIndex = 0; colIndex < 9; colIndex++) {
const num = card[rowIndex][colIndex];
const xCenter = cardX + colIndex * cellWidth + cellWidth / 2;
const yCenter = cardY + (2 - rowIndex) * cellHeight + cellHeight / 2;
page.drawRectangle({
x: cardX + colIndex * cellWidth,
y: cardY + (2 - rowIndex) * cellHeight,
width: cellWidth,
height: cellHeight,
borderColor: blackColor,
borderWidth: innerBorder,
});
if (num !== 0) {
const text = num.toString();
const textWidth = font.widthOfTextAtSize(text, fontSize);
const x = xCenter - textWidth / 2;
let k;
if (fontSize <= 22) {
k = 0.35;
} else if (fontSize <= 28) {
k = 0.35 - (fontSize - 22) * (0.05 / 6);
} else {
k = 0.30 - (fontSize - 28) * (0.05 / 8);
}
const y = yCenter - fontSize * k;
page.drawText(text, {
x: x,
y: y,
size: fontSize,
font: font,
color: blackColor,
});
}
}
}
// --- МЕТКА ВНИЗУ КАРТОЧКИ ---
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
const dateTimeStr = `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}`;
const numberStr = `${globalCardCounter}`; // Используем общий счётчик
const footerY = cardY + footerMargin;
// Отрисовка даты-времени
const dateTimeTextWidth = font.widthOfTextAtSize(dateTimeStr, dateTimeFontSize);
const dateTimeX = cardX + (cardWidth - dateTimeTextWidth - 5) / 2;
page.drawText(dateTimeStr, {
x: dateTimeX,
y: footerY,
size: dateTimeFontSize,
font: font,
color: blackColor,
});
// Отрисовка номера
const numberTextWidth = font.widthOfTextAtSize(numberStr, numberFontSize);
const numberX = dateTimeX + dateTimeTextWidth + 3;
page.drawText(numberStr, {
x: numberX,
y: footerY,
size: numberFontSize,
font: font,
color: blackColor,
});
globalCardCounter++; // Увеличиваем счётчик ПОСЛЕ отрисовки карточки
}
}
// Сохранение и скачивание PDF
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'loto_cards.pdf';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
// Очистка: удаление временного URL и ссылки
URL.revokeObjectURL(url);
document.body.removeChild(link);
} catch (error) {
console.error('Ошибка при генерации PDF:', error);
alert('Произошла ошибка при создании PDF. Проверьте консоль для деталей.');
}
};
</script>
</body>
</html>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
не работает выгрузка в pdf